// celx_observer.cpp // // Copyright (C) 2003-2008, the Celestia Development Team // // Lua script extensions for Celestia: observer object // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. #include #include "celx.h" #include "celx_internal.h" #include "celx_observer.h" //#include //#include #include #include #include using namespace std; using namespace Eigen; using namespace celmath; using celestia::util::GetLogger; // ==================== Observer ==================== int observer_new(lua_State* l, Observer* o) { CelxLua celx(l); Observer** ud = static_cast(lua_newuserdata(l, sizeof(Observer*))); *ud = o; celx.setClass(Celx_Observer); return 1; } Observer* to_observer(lua_State* l, int index) { CelxLua celx(l); Observer** o = static_cast(lua_touserdata(l, index)); CelestiaCore* appCore = celx.appCore(AllErrors); // Check if pointer is still valid, i.e. is used by a view: if (o != nullptr && getViewByObserver(appCore, *o) != nullptr) { return *o; } return nullptr; } static Observer* this_observer(lua_State* l) { CelxLua celx(l); Observer* obs = to_observer(l, 1); if (obs == nullptr) { celx.doError("Bad observer object (maybe tried to access a deleted view?)!"); } return obs; } static int observer_isvalid(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected for observer:isvalid()"); lua_pushboolean(l, to_observer(l, 1) != nullptr); return 1; } static int observer_tostring(lua_State* l) { lua_pushstring(l, "[Observer]"); return 1; } static int observer_setposition(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for setpos"); Observer* o = this_observer(l); UniversalCoord* uc = celx.toPosition(2); if (uc == nullptr) { celx.doError("Argument to observer:setposition must be a position"); return 0; } o->setPosition(*uc); return 0; } static int observer_setorientation(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for setorientation"); Observer* o = this_observer(l); auto q = celx.toRotation(2); if (q == nullptr) { celx.doError("Argument to observer:setorientation must be a rotation"); return 0; } o->setOrientation(*q); return 0; } static int observer_getorientation(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to observer:getorientation()"); Observer* o = this_observer(l); celx.newRotation(o->getOrientation()); return 1; } static int observer_rotate(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for rotate"); Observer* o = this_observer(l); auto q = celx.toRotation(2); if (q == nullptr) { celx.doError("Argument to observer:setpos must be a rotation"); return 0; } o->rotate(q->cast()); return 0; } static int observer_orbit(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for orbit"); Observer* o = this_observer(l); auto q = celx.toRotation(2); if (q == nullptr) { celx.doError("Argument for observer:orbit must be a rotation"); return 0; } o->orbit(Selection(), q->cast()); return 0; } static int observer_lookat(lua_State* l) { CelxLua celx(l); celx.checkArgs(3, 4, "Two or three arguments required for lookat"); int argc = lua_gettop(l); Observer* o = this_observer(l); UniversalCoord* from = nullptr; UniversalCoord* to = nullptr; Vector3d* upd = nullptr; if (argc == 3) { to = celx.toPosition(2); upd = celx.toVector(3); if (to == nullptr) { celx.doError("Argument 1 (of 2) to observer:lookat must be of type position"); return 0; } } else { if (argc == 4) { from = celx.toPosition(2); to = celx.toPosition(3); upd = celx.toVector(4); if (to == nullptr || from == nullptr) { celx.doError("Argument 1 and 2 (of 3) to observer:lookat must be of type position"); return 0; } } } if (upd == nullptr) { celx.doError("Last argument to observer:lookat must be of type vector"); return 0; } Vector3d nd; if (from == nullptr) { nd = to->offsetFromKm(o->getPosition()); } else { nd = to->offsetFromKm(*from); } Vector3f up = upd->cast(); Vector3f n = nd.cast().normalized(); Vector3f v = n.cross(up).normalized(); Vector3f u = v.cross(n); Matrix3f m; m.row(0) = v; m.row(1) = u; m.row(2) = n * (-1); o->setOrientation(Quaternionf(m)); return 0; } static int observer_gototable(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "Expected one table as argument to goto"); Observer* o = this_observer(l); if (!lua_istable(l, 2)) { lua_pushstring(l, "Argument to goto must be a table"); } Observer::JourneyParams jparams; jparams.duration = Observer::JourneyDuration; jparams.from = o->getPosition(); jparams.to = o->getPosition(); jparams.initialOrientation = o->getOrientation(); jparams.finalOrientation = o->getOrientation(); jparams.startInterpolation = Observer::StartInterpolation; jparams.endInterpolation = Observer::EndInterpolation; jparams.accelTime = Observer::AccelerationTime; jparams.traj = Observer::Linear; lua_pushstring(l, "duration"); lua_gettable(l, 2); jparams.duration = celx.safeGetNumber(3, NoErrors, "", Observer::JourneyDuration); lua_settop(l, 2); lua_pushstring(l, "from"); lua_gettable(l, 2); UniversalCoord* from = celx.toPosition(3); if (from != nullptr) jparams.from = *from; lua_settop(l, 2); lua_pushstring(l, "to"); lua_gettable(l, 2); UniversalCoord* to = celx.toPosition(3); if (to != nullptr) jparams.to = *to; lua_settop(l, 2); lua_pushstring(l, "initialOrientation"); lua_gettable(l, 2); auto rot1 = celx.toRotation(3); if (rot1 != nullptr) jparams.initialOrientation = *rot1; lua_settop(l, 2); lua_pushstring(l, "finalOrientation"); lua_gettable(l, 2); auto rot2 = celx.toRotation(3); if (rot2 != nullptr) jparams.finalOrientation = *rot2; lua_settop(l, 2); lua_pushstring(l, "startInterpolation"); lua_gettable(l, 2); jparams.startInterpolation = celx.safeGetNumber(3, NoErrors, "", Observer::StartInterpolation); lua_settop(l, 2); lua_pushstring(l, "endInterpolation"); lua_gettable(l, 2); jparams.endInterpolation = celx.safeGetNumber(3, NoErrors, "", Observer::EndInterpolation); lua_settop(l, 2); lua_pushstring(l, "accelTime"); lua_gettable(l, 2); jparams.accelTime = celx.safeGetNumber(3, NoErrors, "", Observer::AccelerationTime); lua_settop(l, 2); jparams.duration = max(0.0, jparams.duration); jparams.accelTime = min(1.0, max(0.1, jparams.accelTime)); jparams.startInterpolation = min(1.0, max(0.0, jparams.startInterpolation)); jparams.endInterpolation = min(1.0, max(0.0, jparams.endInterpolation)); // args are in universal coords, let setFrame handle conversion: auto tmp = o->getFrame(); o->setFrame(ObserverFrame::Universal, Selection()); o->gotoJourney(jparams); o->setFrame(tmp); return 0; } // First argument is the target object or position; optional second argument // is the travel time static int observer_goto(lua_State* l) { CelxLua celx(l); if (lua_gettop(l) == 2 && lua_istable(l, 2)) { // handle this in own function return observer_gototable(l); } celx.checkArgs(1, 6, "One to five arguments expected to observer:goto"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); UniversalCoord* uc = celx.toPosition(2); if (sel == nullptr && uc == nullptr) { celx.doError("First arg to observer:gotoobject must be object or position"); return 0; } double travelTime, startInter, endInter, accelTime; travelTime = celx.safeGetNumber(3, WrongType, "Second arg to observer:gotoobject must be a number", Observer::JourneyDuration); startInter = celx.safeGetNumber(4, WrongType, "Third arg to observer:gotoobject must be a number", Observer::StartInterpolation); endInter = celx.safeGetNumber(5, WrongType, "Fourth arg to observer:gotoobject must be a number", Observer::EndInterpolation); accelTime = celx.safeGetNumber(6, WrongType, "Fifth arg to observer:goto must be a number", Observer::AccelerationTime); if (startInter < 0.0 || startInter > 1.0) startInter = Observer::StartInterpolation; if (endInter < 0.0 || endInter > 1.0) endInter = Observer::EndInterpolation; // The first argument may be either an object or a position if (sel != nullptr) { o->gotoSelection(*sel, travelTime, startInter, endInter, accelTime, Vector3f::UnitY(), ObserverFrame::ObserverLocal); } else { o->gotoLocation(*uc, o->getOrientation(), travelTime); } return 0; } static int observer_gotolonglat(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 7, "One to five arguments expected to observer:gotolonglat"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First arg to observer:gotolonglat must be an object"); return 0; } double defaultDistance = sel->radius() * 5.0; double longitude = celx.safeGetNumber(3, WrongType, "Second arg to observer:gotolonglat must be a number", 0.0); double latitude = celx.safeGetNumber(4, WrongType, "Third arg to observer:gotolonglat must be a number", 0.0); double distance = celx.safeGetNumber(5, WrongType, "Fourth arg to observer:gotolonglat must be a number", defaultDistance); double travelTime = celx.safeGetNumber(6, WrongType, "Fifth arg to observer:gotolonglat must be a number", 5.0); //distance = distance / KM_PER_LY; Vector3f up = Vector3f::UnitY(); if (lua_gettop(l) >= 7) { auto uparg = celx.toVector(7); if (uparg == nullptr) { celx.doError("Sixth argument to observer:gotolonglat must be a vector"); return 0; } up = uparg->cast(); } o->gotoSelectionLongLat(*sel, travelTime, distance, (float)longitude, (float)latitude, up); return 0; } // deprecated: wrong name, bad interface. static int observer_gotolocation(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 3,"Expected one or two arguments to observer:gotolocation"); Observer* o = this_observer(l); double travelTime = celx.safeGetNumber(3, WrongType, "Second arg to observer:gotolocation must be a number", 5.0); if (travelTime < 0) travelTime = 0.0; UniversalCoord* uc = celx.toPosition(2); if (uc != nullptr) { o->gotoLocation(*uc, o->getOrientation(), travelTime); } else { celx.doError("First arg to observer:gotolocation must be a position"); } return 0; } static int observer_gotodistance(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 5, "One to four arguments expected to observer:gotodistance"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First arg to observer:gotodistance must be object"); return 0; } double distance = celx.safeGetNumber(3, WrongType, "Second arg to observer:gotodistance must be a number", 20000); double travelTime = celx.safeGetNumber(4, WrongType, "Third arg to observer:gotodistance must be a number", 5.0); Vector3f up = Vector3f::UnitY(); if (lua_gettop(l) > 4) { auto up_arg = celx.toVector(5); if (up_arg == nullptr) { celx.doError("Fourth arg to observer:gotodistance must be a vector"); return 0; } up = up_arg->cast(); } o->gotoSelection(*sel, travelTime, distance, up, ObserverFrame::Universal); return 0; } static int observer_gotosurface(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 3, "One to two arguments expected to observer:gotosurface"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First arg to observer:gotosurface must be object"); return 0; } double travelTime = celx.safeGetNumber(3, WrongType, "Second arg to observer:gotosurface must be a number", 5.0); // This is needed because gotoSurface expects frame to be geosync: o->geosynchronousFollow(*sel); o->gotoSurface(*sel, travelTime); return 0; } static int observer_center(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 3, "Expected one or two arguments for to observer:center"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:center must be an object"); return 0; } double travelTime = celx.safeGetNumber(3, WrongType, "Second arg to observer:center must be a number", 5.0); o->centerSelection(*sel, travelTime); return 0; } static int observer_centerorbit(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 3, "Expected one or two arguments for to observer:center"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:centerorbit must be an object"); return 0; } double travelTime = celx.safeGetNumber(3, WrongType, "Second arg to observer:centerorbit must be a number", 5.0); o->centerSelectionCO(*sel, travelTime); return 0; } static int observer_cancelgoto(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "Expected no arguments to observer:cancelgoto"); Observer* o = this_observer(l); o->cancelMotion(); return 0; } static int observer_follow(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:follow"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:follow must be an object"); return 0; } o->follow(*sel); return 0; } static int observer_synchronous(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:synchronous"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:synchronous must be an object"); return 0; } o->geosynchronousFollow(*sel); return 0; } static int observer_lock(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:lock"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:phaseLock must be an object"); return 0; } o->phaseLock(*sel); return 0; } static int observer_chase(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:chase"); Observer* o = this_observer(l); Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:chase must be an object"); return 0; } o->chase(*sel); return 0; } static int observer_track(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:track"); Observer* o = this_observer(l); // If the argument is nil, clear the tracked object if (lua_isnil(l, 2)) { o->setTrackedObject(Selection()); } else { // Otherwise, turn on tracking and set the tracked object Selection* sel = celx.toObject(2); if (sel == nullptr) { celx.doError("First argument to observer:center must be an object"); return 0; } o->setTrackedObject(*sel); } return 0; } static int observer_gettrackedobject(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to observer:gettrackedobject"); Observer* o = this_observer(l); celx.newObject(o->getTrackedObject()); return 1; } // Return true if the observer is still moving as a result of a goto, center, // or similar command. static int observer_travelling(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to observer:travelling"); Observer* o = this_observer(l); if (o->getMode() == Observer::Travelling) lua_pushboolean(l, 1); else lua_pushboolean(l, 0); return 1; } // Return the observer's current time as a Julian day number static int observer_gettime(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to observer:gettime"); Observer* o = this_observer(l); lua_pushnumber(l, o->getTime()); return 1; } // Return the observer's current position static int observer_getposition(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected to observer:getposition"); Observer* o = this_observer(l); celx.newPosition(o->getPosition()); return 1; } static int observer_getsurface(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "One argument expected to observer:getsurface()"); Observer* obs = this_observer(l); lua_pushstring(l, obs->getDisplayedSurface().c_str()); return 1; } static int observer_setsurface(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected to observer:setsurface()"); Observer* obs = this_observer(l); const char* s = lua_tostring(l, 2); if (s == nullptr) obs->setDisplayedSurface(""); else obs->setDisplayedSurface(s); return 0; } static int observer_getframe(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected for observer:getframe()"); Observer* obs = this_observer(l); auto frame = obs->getFrame(); celx.newFrame(*frame); return 1; } static int observer_setframe(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for observer:setframe()"); Observer* obs = this_observer(l); ObserverFrame* frame; frame = celx.toFrame(2); if (frame != nullptr) { obs->setFrame(std::shared_ptr(new ObserverFrame(*frame))); } else { celx.doError("Argument to observer:setframe must be a frame"); } return 0; } static int observer_setspeed(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument required for observer:setspeed()"); Observer* obs = this_observer(l); double speed = celx.safeGetNumber(2, AllErrors, "First argument to observer:setspeed must be a number"); obs->setTargetSpeed((float) astro::microLightYearsToKilometers(speed)); return 0; } static int observer_getspeed(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No argument expected for observer:getspeed()"); Observer* obs = this_observer(l); lua_pushnumber(l, (lua_Number) astro::kilometersToMicroLightYears(obs->getTargetSpeed())); return 1; } static int observer_setfov(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected to observer:setfov()"); Observer* obs = this_observer(l); double fov = celx.safeGetNumber(2, AllErrors, "Argument to observer:setfov() must be a number"); if ((fov >= degToRad(0.001f)) && (fov <= degToRad(120.0f))) { obs->setFOV((float) fov); celx.appCore(AllErrors)->setZoomFromFOV(); } return 0; } static int observer_getfov(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No argument expected to observer:getfov()"); Observer* obs = this_observer(l); lua_pushnumber(l, obs->getFOV()); return 1; } static int observer_splitview(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 3, "One or two arguments expected for observer:splitview()"); Observer* obs = this_observer(l); CelestiaCore* appCore = celx.appCore(AllErrors); const char* splitType = celx.safeGetString(2, AllErrors, "First argument to observer:splitview() must be a string"); View::Type type = (compareIgnoringCase(splitType, "h") == 0) ? View::HorizontalSplit : View::VerticalSplit; double splitPos = celx.safeGetNumber(3, WrongType, "Number expected as argument to observer:splitview()", 0.5); if (splitPos < 0.1) splitPos = 0.1; if (splitPos > 0.9) splitPos = 0.9; View* view = getViewByObserver(appCore, obs); appCore->splitView(type, view, (float)splitPos); return 0; } static int observer_deleteview(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No argument expected for observer:deleteview()"); Observer* obs = this_observer(l); CelestiaCore* appCore = celx.appCore(AllErrors); View* view = getViewByObserver(appCore, obs); appCore->deleteView(view); return 0; } static int observer_singleview(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No argument expected for observer:singleview()"); Observer* obs = this_observer(l); CelestiaCore* appCore = celx.appCore(AllErrors); View* view = getViewByObserver(appCore, obs); appCore->singleView(view); return 0; } static int observer_makeactiveview(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No argument expected for observer:makeactiveview()"); Observer* obs = this_observer(l); CelestiaCore* appCore = celx.appCore(AllErrors); View* view = getViewByObserver(appCore, obs); appCore->setActiveView(view); return 0; } static int observer_equal(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "Wrong number of arguments for comparison!"); Observer* o1 = this_observer(l); Observer* o2 = to_observer(l, 2); lua_pushboolean(l, (o1 == o2)); return 1; } static int observer_setlocationflags(lua_State* l) { CelxLua celx(l); celx.checkArgs(2, 2, "One argument expected for observer:setlocationflags()"); Observer* obs = this_observer(l); if (!lua_istable(l, 2)) { celx.doError("Argument to observer:setlocationflags() must be a table"); return 0; } lua_pushnil(l); auto locationFlags = obs->getLocationFilter(); while (lua_next(l, -2) != 0) { string key; bool value = false; if (lua_isstring(l, -2)) { key = lua_tostring(l, -2); } else { celx.doError("Keys in table-argument to observer:setlocationflags() must be strings"); return 0; } if (lua_isboolean(l, -1)) { value = lua_toboolean(l, -1) != 0; } else { celx.doError("Values in table-argument to observer:setlocationflags() must be boolean"); return 0; } auto &LocationFlagMap = celx.appCore(AllErrors)->scriptMaps()->LocationFlagMap; if (LocationFlagMap.count(key) == 0) { GetLogger()->warn("Unknown key: {}\n", key); } else { const auto flag = LocationFlagMap[key]; if (value) { locationFlags |= flag; } else { locationFlags &= ~flag; } } lua_pop(l,1); } obs->setLocationFilter(locationFlags); return 0; } static int observer_getlocationflags(lua_State* l) { CelxLua celx(l); celx.checkArgs(1, 1, "No arguments expected for observer:getlocationflags()"); Observer* obs = this_observer(l); lua_newtable(l); const auto locationFlags = obs->getLocationFilter(); auto &LocationFlagMap = celx.appCore(AllErrors)->scriptMaps()->LocationFlagMap; for (const auto& it : LocationFlagMap) { lua_pushstring(l, it.first.c_str()); lua_pushboolean(l, (it.second & locationFlags) != 0); lua_settable(l, -3); } return 1; } void CreateObserverMetaTable(lua_State* l) { CelxLua celx(l); celx.createClassMetatable(Celx_Observer); celx.registerMethod("__tostring", observer_tostring); celx.registerMethod("isvalid", observer_isvalid); #if LUA_VERSION_NUM < 502 celx.registerMethod("goto", observer_goto); #endif celx.registerMethod("gotoobject", observer_goto); celx.registerMethod("gotolonglat", observer_gotolonglat); celx.registerMethod("gotolocation", observer_gotolocation); celx.registerMethod("gotodistance", observer_gotodistance); celx.registerMethod("gotosurface", observer_gotosurface); celx.registerMethod("cancelgoto", observer_cancelgoto); celx.registerMethod("setposition", observer_setposition); celx.registerMethod("lookat", observer_lookat); celx.registerMethod("setorientation", observer_setorientation); celx.registerMethod("getorientation", observer_getorientation); celx.registerMethod("getspeed", observer_getspeed); celx.registerMethod("setspeed", observer_setspeed); celx.registerMethod("getfov", observer_getfov); celx.registerMethod("setfov", observer_setfov); celx.registerMethod("rotate", observer_rotate); celx.registerMethod("orbit", observer_orbit); celx.registerMethod("center", observer_center); celx.registerMethod("centerorbit", observer_centerorbit); celx.registerMethod("follow", observer_follow); celx.registerMethod("synchronous", observer_synchronous); celx.registerMethod("chase", observer_chase); celx.registerMethod("lock", observer_lock); celx.registerMethod("track", observer_track); celx.registerMethod("gettrackedobject", observer_gettrackedobject); celx.registerMethod("travelling", observer_travelling); celx.registerMethod("getframe", observer_getframe); celx.registerMethod("setframe", observer_setframe); celx.registerMethod("gettime", observer_gettime); celx.registerMethod("getposition", observer_getposition); celx.registerMethod("getsurface", observer_getsurface); celx.registerMethod("setsurface", observer_setsurface); celx.registerMethod("splitview", observer_splitview); celx.registerMethod("deleteview", observer_deleteview); celx.registerMethod("singleview", observer_singleview); celx.registerMethod("makeactiveview", observer_makeactiveview); celx.registerMethod("getlocationflags", observer_getlocationflags); celx.registerMethod("setlocationflags", observer_setlocationflags); celx.registerMethod("__eq", observer_equal); lua_pop(l, 1); // remove metatable from stack }