929 lines
23 KiB
C++
929 lines
23 KiB
C++
/*
|
||
* Copyright (C) 2002,2003,2004 Daniel Heck
|
||
*
|
||
* 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.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License along
|
||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
*
|
||
*/
|
||
#include "lua.hh"
|
||
#include "errors.hh"
|
||
#include "main.hh"
|
||
#include "world.hh"
|
||
#include "config.h"
|
||
#include "video.hh"
|
||
#include "server.hh"
|
||
#include "sound.hh"
|
||
#include "options.hh"
|
||
#include "lev/Index.hh"
|
||
#include "lev/Proxy.hh"
|
||
|
||
#ifndef CXXLUA
|
||
extern "C" {
|
||
#include "lualib.h"
|
||
#include "tolua++.h"
|
||
}
|
||
#else
|
||
#include "lualib.h"
|
||
#include "tolua++.h"
|
||
#endif
|
||
|
||
|
||
#include "lua-display.hh"
|
||
#include "lua-enigma.hh"
|
||
#include "lua-ecl.hh"
|
||
|
||
#include "ecl.hh"
|
||
#include <cassert>
|
||
|
||
#include "nls.hh"
|
||
|
||
using namespace std;
|
||
using namespace enigma;
|
||
using namespace lua;
|
||
|
||
using ecl::round_down;
|
||
using ecl::strf;
|
||
|
||
using enigma::GridPos;
|
||
using world::Object;
|
||
using world::GridObject;
|
||
using world::ForceField;
|
||
|
||
namespace lua
|
||
{
|
||
int EmitSound (lua_State *L);
|
||
int EmitSoundGlobal (lua_State *L);
|
||
int MakeObject (lua_State *L);
|
||
}
|
||
|
||
namespace
|
||
{
|
||
lua_State *level_state = 0; // level-local state
|
||
lua_State *global_state = 0; // global Lua state
|
||
|
||
lua::Error _lua_err_code (int i)
|
||
{
|
||
switch (i) {
|
||
case 0: return NO_LUAERROR;
|
||
case LUA_ERRRUN: return ERRRUN;
|
||
case LUA_ERRFILE: return ERRFILE;
|
||
case LUA_ERRSYNTAX: return ERRSYNTAX;
|
||
case LUA_ERRMEM: return ERRMEM;
|
||
case LUA_ERRERR: return ERRERR;
|
||
}
|
||
assert (!"Should never get there!");
|
||
}
|
||
|
||
void throwLuaError(lua_State * L, const char * message) {
|
||
std::string backtrace = message;
|
||
backtrace += "\nBacktrace:\n";
|
||
lua_Debug dbgInfo;
|
||
int frame = 0;
|
||
while (lua_getstack(L, frame, &dbgInfo)) {
|
||
lua_getinfo(L, "Sl", &dbgInfo);
|
||
if (dbgInfo.source[0] == '@')
|
||
// lua code loaded from file
|
||
backtrace += ecl::strf("#%d %s: %d\n", frame, dbgInfo.source,
|
||
dbgInfo.currentline);
|
||
else if (dbgInfo.source[0] == '-' && dbgInfo.source[1] == '-' &&
|
||
dbgInfo.source[2] == '@') {
|
||
// lua code loaded from string
|
||
std::string code = dbgInfo.source;
|
||
std::string::size_type slashPosFilenameEnd = code.find('\n');
|
||
std::string::size_type slashPosLineStart;
|
||
std::string::size_type slashPosLineEnd = slashPosFilenameEnd;
|
||
for (int i = 1; i < dbgInfo.currentline; i++) {
|
||
slashPosLineStart = slashPosLineEnd;
|
||
slashPosLineEnd = code.find('\n', slashPosLineStart + 1);
|
||
}
|
||
backtrace += ecl::strf("#%d %s: %d\n (%s)\n", frame,
|
||
code.substr(2, slashPosFilenameEnd - 2).c_str(),
|
||
dbgInfo.currentline - 1,
|
||
code.substr(slashPosLineStart + 1, slashPosLineEnd -
|
||
slashPosLineStart - 1).c_str());
|
||
}
|
||
frame++;
|
||
}
|
||
luaL_error(L, backtrace.c_str());
|
||
|
||
}
|
||
}
|
||
|
||
/* -------------------- Helper routines -------------------- */
|
||
|
||
using enigma::Value;
|
||
|
||
void lua::SetTableVar (lua_State *L,
|
||
const char *tablename,
|
||
const char *name,
|
||
double value)
|
||
{
|
||
lua_getglobal (L, tablename);
|
||
lua_pushstring (L, name);
|
||
lua_pushnumber (L, value);
|
||
lua_rawset (L, -3);
|
||
lua_pop (L, 1);
|
||
}
|
||
|
||
static void
|
||
push_value(lua_State *L, const Value &val)
|
||
{
|
||
switch (val.get_type()) {
|
||
case Value::NIL: lua_pushnil(L); break;
|
||
case Value::DOUBLE: lua_pushnumber(L, to_double(val)); break;
|
||
case Value::STRING: lua_pushstring(L, to_string(val)); break;
|
||
}
|
||
}
|
||
|
||
static Value
|
||
to_value(lua_State *L, int idx)
|
||
{
|
||
switch (lua_type(L, idx)) {
|
||
case LUA_TNIL: return Value();
|
||
case LUA_TNUMBER: return Value(lua_tonumber(L,idx));
|
||
case LUA_TSTRING: return Value(lua_tostring(L,idx));
|
||
case LUA_TBOOLEAN: return (lua_toboolean(L,idx)) ? Value(1) : Value();
|
||
default: throwLuaError(L,"Cannot convert type to Value.");
|
||
}
|
||
return Value();
|
||
}
|
||
|
||
static bool
|
||
is_object(lua_State *L, int idx)
|
||
{
|
||
return lua_isuserdata(L,idx) && luaL_checkudata(L,idx,"_ENIGMAOBJECT");
|
||
}
|
||
|
||
static Object *
|
||
to_object(lua_State *L, int idx)
|
||
{
|
||
if (lua_isnil(L,idx))
|
||
return 0;
|
||
|
||
if (!is_object(L,idx)) {
|
||
throwLuaError(L, "Cannot convert type to an Object");
|
||
return 0;
|
||
}
|
||
return static_cast<Object*>(*(static_cast<void**>(lua_touserdata(L,idx))));
|
||
}
|
||
|
||
static void
|
||
pushobject (lua_State *L, Object *obj)
|
||
{
|
||
Object **udata;
|
||
/* Lua does not allow NULL pointers in userdata variables, so
|
||
convert them manually to `nil' values. */
|
||
if (obj == 0)
|
||
lua_pushnil(L);
|
||
else
|
||
{
|
||
udata=(Object**)lua_newuserdata(L,sizeof(int));
|
||
*udata=obj;
|
||
luaL_getmetatable(L, "_ENIGMAOBJECT");
|
||
lua_setmetatable(L, -2);
|
||
}
|
||
}
|
||
|
||
|
||
/* -------------------- Interface routines -------------------- */
|
||
|
||
int lua::MakeObject (lua_State *L)
|
||
{
|
||
const char *name = lua_tostring(L, 1);
|
||
if (!name) {
|
||
throwLuaError(L, "MakeObject: string expected as argument");
|
||
}
|
||
Object *obj = world::MakeObject(name);
|
||
if (obj == NULL)
|
||
throwLuaError(L, ecl::strf("MakeObject: unknown object name '%s'", name).c_str());
|
||
pushobject(L, obj);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_get_object_template(lua_State *L)
|
||
{
|
||
Object *obj = world::GetObjectTemplate(lua_tostring(L, 1));
|
||
pushobject(L, obj);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_set_attrib(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L,1);
|
||
const char *key = lua_tostring(L,2);
|
||
if (obj && key)
|
||
obj->set_attrib(key, to_value(L, 3));
|
||
else
|
||
throwLuaError(L, strf("SetAttrib: invalid object or attribute name '%s'", key).c_str());
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_get_attrib(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L,1);
|
||
const char *key = lua_tostring(L,2);
|
||
|
||
if (!obj) {
|
||
throwLuaError(L, "GetAttrib: invalid object");
|
||
return 0;
|
||
}
|
||
if (!key) {
|
||
throwLuaError(L, "GetAttrib: invalid key");
|
||
return 0;
|
||
}
|
||
|
||
if (0 == strcmp(key, "kind")) {
|
||
throwLuaError(L, "GetAttrib: illegal attribute, use GetKind()");
|
||
return 0;
|
||
}
|
||
|
||
const Value *v = obj->get_attrib(key);
|
||
if (!v) {
|
||
// unknown attribute
|
||
lua_pushnil(L);
|
||
}
|
||
else
|
||
push_value(L, *v);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_get_kind(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L,1);
|
||
|
||
if (!obj) {
|
||
throwLuaError(L, "GetKind: invalid object");
|
||
return 0;
|
||
}
|
||
|
||
push_value(L, Value(obj->get_kind()));
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_is_same_object(lua_State *L)
|
||
{
|
||
Object *obj1 = to_object(L,1);
|
||
Object *obj2 = to_object(L,2);
|
||
|
||
lua_pushboolean(L, obj1 == obj2);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_set_floor(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Floor *fl=0;
|
||
|
||
if (lua_isnil(L, 3))
|
||
fl = 0;
|
||
else if (is_object(L,3)) {
|
||
fl = static_cast<Floor*>(*(static_cast<void**> (lua_touserdata(L,3))));
|
||
if( ! fl)
|
||
throwLuaError(L, "object is no valid floor");
|
||
} else
|
||
throwLuaError(L, "argument 3 must be an Object or nil");
|
||
world::SetFloor(GridPos(x,y), fl);
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_set_item(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Item *it = dynamic_cast<Item*>(to_object(L, 3));
|
||
if( ! it) {
|
||
throwLuaError(L, "object is no valid item");
|
||
}
|
||
world::SetItem(GridPos(x,y), it);
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_set_stone(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Stone *st = dynamic_cast<Stone*>(to_object(L, 3));
|
||
if( ! st)
|
||
throwLuaError(L, "object is no valid stone");
|
||
world::SetStone(GridPos(x,y), st);
|
||
return 0;
|
||
}
|
||
|
||
static int en_kill_stone(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
world::KillStone(GridPos(x,y));
|
||
return 0;
|
||
}
|
||
|
||
static int en_kill_item(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
world::KillItem(GridPos(x,y));
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_set_actor(lua_State *L)
|
||
{
|
||
double x = lua_tonumber(L,1);
|
||
double y = lua_tonumber(L,2);
|
||
Actor *ac = dynamic_cast<Actor*>(to_object(L, 3));
|
||
if( ! ac)
|
||
throwLuaError(L, "object is no valid actor");
|
||
if (world::IsInsideLevel(GridPos(round_down<int>(x), round_down<int>(y))))
|
||
world::AddActor(x, y, ac);
|
||
else
|
||
throwLuaError(L, "position is outside of world");
|
||
return 0;
|
||
}
|
||
|
||
|
||
static int
|
||
en_send_message(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L, 1);
|
||
const char *msg = lua_tostring(L, 2);
|
||
Value v;
|
||
if (!msg)
|
||
throwLuaError(L,"Illegal message");
|
||
else if (obj) {
|
||
try {
|
||
v = world::SendMessage (obj, msg, to_value(L, 3));
|
||
}
|
||
catch (const XLevelRuntime &e) {
|
||
throwLuaError (L, e.what());
|
||
}
|
||
catch (...) {
|
||
throwLuaError (L, "uncaught exception");
|
||
}
|
||
}
|
||
push_value(L, v);
|
||
return 0;
|
||
}
|
||
|
||
int lua::EmitSound (lua_State *L)
|
||
{
|
||
Object *obj = to_object(L, 1);
|
||
const char *soundname = lua_tostring(L, 2);
|
||
double vol = 1.0;
|
||
|
||
if (lua_isnumber(L, 3))
|
||
vol = lua_tonumber(L, 3);
|
||
if (!soundname)
|
||
throwLuaError(L,"Illegal sound");
|
||
else if (obj) {
|
||
GridObject *gobj = dynamic_cast<GridObject*>(obj);
|
||
if (gobj) {
|
||
if (!gobj->sound_event (soundname, vol)) {
|
||
//throwLuaError(L, strf("Can't find sound '%s'", soundname).c_str());
|
||
// Don't throw an error when no sound file was found.
|
||
// Remember that user sound sets might be incomplete, and
|
||
// absolutely correct levels could throw an error here.
|
||
// Instead, write the "silence string" to the command line:
|
||
sound::WriteSilenceString(soundname);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
throwLuaError(L, "EmitSound: invalid object");
|
||
|
||
return 0;
|
||
}
|
||
|
||
int lua::EmitSoundGlobal (lua_State *L)
|
||
{
|
||
const char *soundname = lua_tostring(L, 1);
|
||
double vol = 1.0;
|
||
|
||
if (lua_isnumber(L, 3))
|
||
vol = lua_tonumber(L, 3);
|
||
if (!soundname)
|
||
throwLuaError(L,"Illegal sound");
|
||
else
|
||
sound::EmitSoundEventGlobal(soundname, vol);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_name_object(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L, 1);
|
||
const char *name = lua_tostring(L, 2);
|
||
|
||
if (!obj)
|
||
throwLuaError(L, "NameObject: Illegal object");
|
||
else if (!name)
|
||
throwLuaError(L, "NameObject: Illegal name");
|
||
else
|
||
world::NameObject(obj, name);
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_get_named_object(lua_State *L)
|
||
{
|
||
Object *o = world::GetNamedObject(lua_tostring(L,1));
|
||
pushobject(L, o);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_get_floor(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Object *o = world::GetFloor(GridPos(x, y));
|
||
pushobject(L, o);
|
||
return 1;
|
||
}
|
||
static int
|
||
en_get_item(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Object *o = world::GetItem(GridPos(x, y));
|
||
pushobject(L, o);
|
||
return 1;
|
||
}
|
||
static int
|
||
en_get_stone(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
Object *o = world::GetStone(GridPos(x, y));
|
||
pushobject(L, o);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_get_pos(lua_State *L)
|
||
{
|
||
Object *obj = to_object(L, 1);
|
||
GridPos p;
|
||
|
||
if (!obj) {
|
||
throwLuaError(L, "GetPos: Illegal object");
|
||
return 0;
|
||
}
|
||
|
||
if (GridObject *gobj = dynamic_cast<GridObject*>(obj))
|
||
p = gobj->get_pos();
|
||
else if (Actor *a = dynamic_cast<Actor*>(obj)) {
|
||
p = GridPos(a->get_pos());
|
||
}
|
||
else
|
||
p = GridPos(-1, -1);
|
||
|
||
lua_pushnumber(L, double(p.x));
|
||
lua_pushnumber(L, double(p.y));
|
||
return 2;
|
||
}
|
||
|
||
static int en_add_constant_force(lua_State *L) {
|
||
ecl::V2 v;
|
||
v[0] = lua_tonumber(L, 1);
|
||
v[1] = lua_tonumber(L, 2);
|
||
world::SetConstantForce (v);
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_add_rubber_band (lua_State *L)
|
||
{
|
||
Actor *a1 = dynamic_cast<Actor*> (to_object(L, 1));
|
||
Object *o2 = to_object(L, 2);
|
||
Actor *a2 = dynamic_cast<Actor*>(o2);
|
||
Stone *st = dynamic_cast<Stone*>(o2);
|
||
world::RubberBandData d;
|
||
d.strength = lua_tonumber (L, 3);
|
||
d.length = lua_tonumber (L, 4);
|
||
d.minlength = lua_tonumber (L, 5);
|
||
|
||
if (!a1)
|
||
throwLuaError(L, "AddRubberBand: First argument must be an actor\n");
|
||
else {
|
||
if (a2)
|
||
world::AddRubberBand (a1, a2, d);
|
||
else if (st)
|
||
world::AddRubberBand (a1, st, d);
|
||
else
|
||
throwLuaError(L, "AddRubberBand: Second argument must be actor or stone\n");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_get_ticks(lua_State *L)
|
||
{
|
||
lua_pushnumber(L, SDL_GetTicks());
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_is_solved(lua_State *L)
|
||
{
|
||
// Function depreceated
|
||
// - filename is no longer a useful reference for levels
|
||
// - levels should not depend on external data for reasons of
|
||
// network compatibility and level journaling
|
||
throwLuaError(L, "Usage of depreceated function \"IsSolved()\"");
|
||
|
||
// const char *levelname = lua_tostring(L,1);
|
||
// int solved = 0;
|
||
//
|
||
// levels::Level level (0, 0);
|
||
// if (levels::FindLevel (levelname, level)) {
|
||
// solved = level.is_solved(options::GetInt("Difficulty"));
|
||
// } else
|
||
// if (solved)
|
||
// lua_pushnumber(L, solved);
|
||
// else
|
||
// lua_pushnil(L);
|
||
return 1;
|
||
}
|
||
|
||
static int
|
||
en_add_scramble(lua_State *L)
|
||
{
|
||
int x = round_down<int>(lua_tonumber(L, 1));
|
||
int y = round_down<int>(lua_tonumber(L, 2));
|
||
const char *dir = lua_tostring(L,3);
|
||
const char *allowed = "wsen";
|
||
const char *found = strchr(allowed, dir[0]);
|
||
|
||
if (found && found[0])
|
||
world::AddScramble(GridPos(x,y), enigma::Direction(found-allowed));
|
||
else
|
||
throwLuaError(L, "AddScramble: Third argument must be one character of \"wsen\"");
|
||
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_set_scramble_intensity(lua_State *L)
|
||
{
|
||
world::SetScrambleIntensity(int(lua_tonumber(L, 1)));
|
||
return 0;
|
||
}
|
||
|
||
static int
|
||
en_add_signal (lua_State *L) {
|
||
const char *sourcestr = lua_tostring (L, 1);
|
||
const char *targetstr = lua_tostring (L, 2);
|
||
const char *msg = lua_tostring (L, 3);
|
||
|
||
using namespace enigma;
|
||
GridLoc source, target;
|
||
|
||
if (sourcestr == 0 || !to_gridloc(sourcestr, source))
|
||
throwLuaError (L, "AddSignal: invalid source descriptor");
|
||
if (targetstr == 0 || !to_gridloc(targetstr, target))
|
||
throwLuaError (L, "AddSignal: invalid target descriptor");
|
||
if (msg == 0)
|
||
msg = "signal";
|
||
|
||
world::AddSignal (source, target, msg);
|
||
return 0;
|
||
}
|
||
|
||
int loadLib(lua_State *L)
|
||
{
|
||
const char *id = lua_tostring(L, 1);
|
||
lev::Proxy * curProxy = lev::Index::getCurrentProxy();
|
||
try {
|
||
curProxy->loadDependency(id);
|
||
} catch (XLevelLoading &err) {
|
||
throwLuaError(L, err.what());
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
static CFunction globalfuncs[] = {
|
||
{FindDataFile, "FindDataFile"},
|
||
// {lua::PlaySoundGlobal, "PlaySoundGlobal"},
|
||
// {lua::PlaySound, "PlaySound"},
|
||
{en_get_ticks, "GetTicks"},
|
||
{0,0}
|
||
};
|
||
|
||
static CFunction levelfuncs[] = {
|
||
|
||
// internal functions
|
||
|
||
{FindDataFile, "FindDataFile"},
|
||
{loadLib, "LoadLib"},
|
||
{en_get_object_template,"GetObjectTemplate"},
|
||
{lua::MakeObject, "MakeObject"},
|
||
{en_set_actor, "SetActor"},
|
||
|
||
// finding objects
|
||
|
||
{en_get_named_object, "GetNamedObject"},
|
||
|
||
{en_get_floor, "GetFloor"},
|
||
{en_get_item, "GetItem"},
|
||
{en_get_stone, "GetStone"},
|
||
|
||
// information from objects
|
||
|
||
{en_get_pos, "GetPos"},
|
||
{en_get_attrib, "GetAttrib"},
|
||
{en_get_kind, "GetKind"},
|
||
{en_is_same_object, "IsSameObject"},
|
||
|
||
// manipulating objects
|
||
|
||
{en_set_attrib, "SetAttrib"},
|
||
{en_send_message, "SendMessage"},
|
||
{en_name_object, "NameObject"},
|
||
|
||
// sound effects
|
||
|
||
{lua::EmitSound, "EmitSound"},
|
||
|
||
// manipulating level
|
||
|
||
{en_set_floor, "SetFloor"},
|
||
{en_set_item, "SetItem"},
|
||
{en_set_stone, "SetStone"},
|
||
|
||
{en_kill_stone, "KillStone"},
|
||
{en_kill_item, "KillItem"},
|
||
|
||
|
||
// signals
|
||
{en_add_signal, "AddSignal"},
|
||
|
||
// access/modify global data
|
||
|
||
{en_get_ticks, "GetTicks"},
|
||
{en_is_solved, "IsSolved"},
|
||
|
||
{en_add_constant_force, "AddConstantForce"},
|
||
{en_add_rubber_band, "AddRubberBand"},
|
||
{en_add_scramble, "AddScramble"},
|
||
{en_set_scramble_intensity, "SetScrambleIntensity"},
|
||
|
||
|
||
{0,0}
|
||
};
|
||
|
||
|
||
/* -------------------- lua:: functions -------------------- */
|
||
|
||
int lua::FindDataFile (lua_State *L)
|
||
{
|
||
const char *filename = lua_tostring(L, 1);
|
||
string absfile;
|
||
if (app.resourceFS->findFile(filename, absfile))
|
||
lua_pushstring(L, absfile.c_str());
|
||
else
|
||
lua_pushnil (L);
|
||
return 1;
|
||
}
|
||
|
||
void lua::RegisterFuncs(lua_State *L, CFunction *funcs)
|
||
{
|
||
lua_getglobal(L, "enigma");
|
||
for (unsigned i=0; funcs[i].func; ++i) {
|
||
lua_pushstring(L, funcs[i].name);
|
||
lua_pushcfunction(L, funcs[i].func);
|
||
lua_settable(L, -3);
|
||
}
|
||
lua_pop(L, 1);
|
||
}
|
||
|
||
Error lua::CallFunc(lua_State *L, const char *funcname, const Value& arg, world::Object *obj) {
|
||
int retval;
|
||
lua_getglobal(L, funcname);
|
||
push_value(L, arg);
|
||
pushobject(L, obj);
|
||
retval=lua_pcall(L,2,0,0);
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
return _lua_err_code(retval);
|
||
}
|
||
|
||
Error lua::CallFunc(lua_State *L, const char *funcname, const ByteVec& arg) {
|
||
int retval;
|
||
lua_getglobal(L, funcname);
|
||
lua_pushlstring (L, &arg[0], arg.size());
|
||
retval=lua_pcall(L,1,0,0);
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
return _lua_err_code(retval);
|
||
}
|
||
|
||
Error lua::DoAbsoluteFile(lua_State *L, const string &filename)
|
||
{
|
||
int oldtop = lua_gettop(L);
|
||
int retval = luaL_loadfile(L, filename.c_str());
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
else
|
||
{
|
||
retval= lua_pcall(L, 0, 0, 0);
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
}
|
||
lua_settop(L, oldtop);
|
||
return _lua_err_code(retval);
|
||
}
|
||
|
||
Error lua::DoGeneralFile(lua_State *L, GameFS * fs, const string &filename)
|
||
{
|
||
string completefn;
|
||
if (fs->findFile(filename, completefn)) {
|
||
return lua::DoAbsoluteFile(L, completefn);
|
||
}
|
||
else {
|
||
return _lua_err_code(LUA_ERRFILE);
|
||
}
|
||
}
|
||
|
||
Error lua::Dofile(lua_State *L, const string &filename)
|
||
{
|
||
return lua::DoGeneralFile(L, app.resourceFS, filename);
|
||
}
|
||
|
||
Error lua::DoSysFile(lua_State *L, const string &filename)
|
||
{
|
||
return lua::DoGeneralFile(L, app.systemFS, filename);
|
||
}
|
||
|
||
void lua::CheckedDoFile (lua_State *L, GameFS * fs, std::string const& fname)
|
||
{
|
||
string completefn;
|
||
if (!fs->findFile(fname, completefn))
|
||
{
|
||
fprintf(stderr, _("Cannot find '%s'.\n"), fname.c_str());
|
||
fprintf(stderr, _("Your installation may be incomplete or invalid.\n"));
|
||
exit (1);
|
||
}
|
||
|
||
lua::Error status = lua::DoAbsoluteFile(L, completefn);
|
||
if (status != lua::NO_LUAERROR) {
|
||
fprintf(stderr, _("There was an error loading '%s'.\n"), completefn.c_str());
|
||
fprintf(stderr, _("Your installation may be incomplete or invalid.\n"));
|
||
fprintf(stderr, _("Error: '%s'\n"), lua::LastError(L).c_str());
|
||
exit (1);
|
||
}
|
||
}
|
||
|
||
|
||
Error lua::Dobuffer (lua_State *L, const ByteVec &luacode) {
|
||
int retval;
|
||
const char *buffer = reinterpret_cast<const char *>(&luacode[0]);
|
||
|
||
retval=luaL_loadbuffer(L, buffer, luacode.size(), "buffer");
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
else
|
||
{
|
||
retval= lua_pcall(L, 0, 0, 0);
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
}
|
||
return _lua_err_code(retval);
|
||
}
|
||
|
||
string lua::LastError (lua_State *L)
|
||
{
|
||
lua_getglobal (L, "_LASTERROR");
|
||
if (lua_isstring(L,-1)){
|
||
return string (lua_tostring (L, -1));
|
||
}
|
||
else {
|
||
return "Lua Error. No error message available.";
|
||
}
|
||
}
|
||
|
||
|
||
Error lua::DoSubfolderfile(lua_State *L, const string &basefolder, const string &filename) {
|
||
std::list <string> fnames = app.resourceFS->findSubfolderFiles(basefolder, filename);
|
||
int retval = 0;
|
||
while (fnames.size() > 0) {
|
||
int oldtop = lua_gettop(L);
|
||
string fname = fnames.front();
|
||
retval = luaL_loadfile(L, fname.c_str());
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
else
|
||
{
|
||
retval= lua_pcall(L, 0, 0, 0);
|
||
if (retval!=0) // error
|
||
{
|
||
lua_setglobal (L, "_LASTERROR") ; //Set _LASTERROR to returned error message
|
||
}
|
||
}
|
||
fnames.pop_front();
|
||
lua_settop(L, oldtop);
|
||
}
|
||
return _lua_err_code(retval);
|
||
}
|
||
|
||
lua_State *lua::GlobalState()
|
||
{
|
||
if (global_state == 0) {
|
||
lua_State *L = global_state = lua_open();
|
||
|
||
luaL_openlibs(L);
|
||
CheckedDoFile(L, app.systemFS, "compat.lua");
|
||
|
||
tolua_open(L);
|
||
tolua_enigma_open(L);
|
||
|
||
RegisterFuncs(L, globalfuncs);
|
||
}
|
||
return global_state;
|
||
}
|
||
|
||
void lua::ShutdownGlobal()
|
||
{
|
||
assert (global_state);
|
||
lua_close(global_state);
|
||
global_state = 0;
|
||
}
|
||
|
||
#ifdef _MSC_VER
|
||
#define snprintf _snprintf
|
||
#endif
|
||
|
||
|
||
|
||
lua_State *lua::InitLevel()
|
||
{
|
||
char buffer[255];
|
||
|
||
lua_State *L = level_state = lua_open();
|
||
luaL_dostring (L, "options={}");
|
||
snprintf (buffer, sizeof(buffer), "options.Difficulty = %d",
|
||
server::GetDifficulty());
|
||
luaL_dostring (L, buffer);
|
||
|
||
luaL_openlibs(L);
|
||
|
||
tolua_open(L);
|
||
tolua_enigma_open(L);
|
||
tolua_px_open(L);
|
||
tolua_display_open(L);
|
||
|
||
RegisterFuncs(L, levelfuncs);
|
||
|
||
// Create a new metatable for world::Object objects
|
||
luaL_newmetatable(L,"_ENIGMAOBJECT");
|
||
return L;
|
||
}
|
||
|
||
lua_State *lua::LevelState ()
|
||
{
|
||
return level_state;
|
||
}
|
||
|
||
void lua::ShutdownLevel() {
|
||
if (level_state) {
|
||
lua_close(level_state);
|
||
level_state = 0;
|
||
}
|
||
}
|
||
|