LuaYard/LuaEngine.cpp
2019-08-10 15:29:42 +08:00

1409 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "LuaEngine.h"
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <cstdlib>
#include "SDL2/include/SDL.h"
#include "SDL2/include/SDL_mixer.h"
#include "SDL2/include/SDL_ttf.h"
#include "SDL2/include/SDL_image.h"
#include "PlatAPI.h"
using namespace std;
// Global single-thread unprotected event callback id counter
int callback_counter = 1;
bool LuaCompareType(lua_State* L, int index, const char* type, bool leave=false)
{
int indexType = lua_getfield(L, index, "type");
if (indexType == LUA_TSTRING)
{
if (strcmp(lua_tostring(L, -1), type) == 0)
{
if(!leave) lua_pop(L, 1);
return true;
}
else
{
if (!leave) lua_pop(L, 1);
return false;
}
}
else
{
lua_pop(L, 1);
if (leave) lua_pushstring(L, lua_typename(L, indexType));
return false;
}
}
void check(lua_State* L, const vector<int>& typearr, const vector<const char*>& userdata_type=vector<const char*>())
{
int sz = typearr.size();
int nextIndex = 0;
int maxIndex = userdata_type.size();
for (int i = 0; i < sz; i++)
{
int thisType = lua_type(L, i + 1);
if (thisType != typearr[i])
{
luaL_error(L, "Bad argument #%d. %s expected, got %s", i + 1, lua_typename(L, typearr[i]), lua_typename(L, lua_type(L, i + 1)));
}
if (maxIndex && thisType == LUA_TUSERDATA)
{
if (nextIndex < maxIndex)
{
if (!LuaCompareType(L, i + 1, userdata_type[nextIndex], true))
{
luaL_error(L, "Bad argument #%d. %s expected, got %s", i + 1, userdata_type[nextIndex], lua_tostring(L, -1));
}
nextIndex++;
}
else
{
luaL_error(L, "Not enough userdata type string.");
}
}
}
}
void ConvertMusicAsyncImpl(const string& filename, const string& targetname, int ticket)
{
string command = "bin\\ffmpeg.exe -y -loglevel error -hide_banner -i \""s + filename + "\" \"" + targetname + "\"";
cout << command << endl;
system(command.c_str());
SDL_Event e;
e.type = SDL_USEREVENT;
e.user.code = 1002; // General zero ret callback.
e.user.data1 = new int(ticket);
e.user.data2 = nullptr;
SDL_PushEvent(&e);
}
static int ConvertMusicAsync(lua_State* L)
{
check(L, { LUA_TSTRING, LUA_TSTRING, LUA_TFUNCTION });
lua_pushfstring(L, "cb%d", callback_counter);
lua_pushvalue(L, 3);
lua_settable(L, LUA_REGISTRYINDEX);
string filename(lua_tostring(L, 1));
string targetname(lua_tostring(L, 2));
thread td(ConvertMusicAsyncImpl, filename, targetname, callback_counter);
td.detach();
lua_pushnumber(L, callback_counter);
callback_counter++;
return 1;
}
struct LuaTimerCallbackData
{
void* p;
int id;
int type; // 0=setTimeout, 1=setInterval
};
Uint32 LuaTimerCallbackGate(Uint32 interval, void* param)
{
SDL_Event e;
e.type = SDL_USEREVENT;
e.user.code = 1001;
e.user.data1 = param;
SDL_PushEvent(&e);
return ((LuaTimerCallbackData*)(param))->type ? interval : 0;
}
int GetMouseState(const char** output, Uint32 state)
{
int i = 0;
if (state & SDL_BUTTON_LMASK)
{
output[i++] = "left";
}
if (state & SDL_BUTTON_MMASK)
{
output[i++] = "middle";
}
if (state & SDL_BUTTON_RMASK)
{
output[i++] = "right";
}
if (state & SDL_BUTTON_X1MASK)
{
output[i++] = "x1";
}
if (state & SDL_BUTTON_X2MASK)
{
output[i++] = "x2";
}
return i;
}
#define setfn(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setfield(L, -2, name)
struct Window
{
SDL_Window* wnd;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Window::close" << endl;
Window* p = (Window*)lua_touserdata(L, 1);
if (p->wnd)
{
// release data table
lua_pushlightuserdata(L, p);
lua_pushnil(L);
lua_settable(L, LUA_REGISTRYINDEX);
SDL_DestroyWindow(p->wnd);
p->wnd = nullptr;
}
return 0;
}
static int start(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Window* p = (Window*)lua_touserdata(L, 1);
lua_settop(L, 1);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
bool running = true;
SDL_Event e;
while (running && SDL_WaitEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEBUTTONDOWN:
{
if (lua_getfield(L, -1, "onmousedown") != LUA_TNIL)
{
lua_pushnumber(L, e.button.x);
lua_pushnumber(L, e.button.y);
lua_pushnumber(L, e.button.button);
const char* arr[8];
int ret = GetMouseState(arr, e.button.state);
lua_newtable(L);
for (int i = 0; i < ret; i++)
{
lua_pushboolean(L, 1);
lua_setfield(L, -2, arr[i]);
}
lua_call(L, 4, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_MOUSEBUTTONUP:
{
if (lua_getfield(L, -1, "onmouseup") != LUA_TNIL)
{
lua_pushnumber(L, e.button.x);
lua_pushnumber(L, e.button.y);
lua_pushnumber(L, e.button.button);
const char* arr[8];
int ret = GetMouseState(arr, e.button.state);
lua_newtable(L);
for (int i = 0; i < ret; i++)
{
lua_pushboolean(L, 1);
lua_setfield(L, -2, arr[i]);
}
lua_call(L, 3, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_MOUSEMOTION:
{
if (lua_getfield(L, -1, "onmousemove") != LUA_TNIL)
{
lua_pushnumber(L, e.motion.x);
lua_pushnumber(L, e.motion.y);
lua_pushnumber(L, e.motion.xrel);
lua_pushnumber(L, e.motion.yrel);
const char* arr[8];
int ret = GetMouseState(arr, e.button.state);
lua_newtable(L);
for (int i = 0; i < ret; i++)
{
lua_pushboolean(L, 1);
lua_setfield(L, -2, arr[i]);
}
lua_call(L, 5, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_QUIT:
{
if (lua_getfield(L, -1, "onquit") != LUA_TNIL)
{
lua_call(L, 0, LUA_MULTRET);
}
else lua_pop(L, 1);
running = 0;
break;
}
case SDL_KEYDOWN:
{
if (lua_getfield(L, -1, "onkeydown") != LUA_TNIL)
{
lua_pushnumber(L, e.key.keysym.sym);
lua_call(L, 1, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_KEYUP:
{
if (lua_getfield(L, -1, "onkeyup") != LUA_TNIL)
{
lua_pushnumber(L, e.key.keysym.sym);
lua_call(L, 1, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case SDL_USEREVENT:
{
switch (e.user.code)
{
case 1001:
{
LuaTimerCallbackData* pd = (LuaTimerCallbackData* )e.user.data1;
lua_pushlightuserdata(L, pd->p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "tmcb%d", pd->id);
lua_gettable(L, -2);
delete pd;
lua_call(L, 0, LUA_MULTRET);
lua_pop(L, 1);
break;
}
case 1002:
{
// ȫ<>ֻص<D6BB>, <20><EFBFBD><ECB2BD><EFBFBD><EFBFBD><EFBFBD>޷<EFBFBD><DEB7><EFBFBD>ֵ
lua_pushfstring(L, "cb%d", *(int*)e.user.data1);
lua_gettable(L, LUA_REGISTRYINDEX);
delete e.user.data1;
lua_call(L, 0, LUA_MULTRET);
break;
}
}
break;
case 1003:
{
// <20><><EFBFBD>ֲ<EFBFBD><D6B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (lua_getfield(L, -1, "onmusicover") != LUA_TNIL)
{
lua_call(L, 0, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
case 1004:
{
// Ƶ<><C6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (lua_getfield(L, -1, "onchannelover") != LUA_TNIL)
{
lua_pushinteger(L, *(int*)e.user.data1);
delete e.user.data1;
lua_call(L, 1, LUA_MULTRET);
}
else lua_pop(L, 1);
break;
}
}
default:
{
cout << e.type << endl;
}
}
}
return 0;
}
static int set_event_handler(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TFUNCTION });
Window* p = (Window*)lua_touserdata(L, 1);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "on%s", lua_tostring(L, 2));
lua_pushvalue(L, 3);
lua_settable(L, -3);
return 0;
}
static int setTimeout(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TFUNCTION, LUA_TNUMBER }); // ms
Window* p = (Window*)lua_touserdata(L, 1);
LuaTimerCallbackData* pd = new LuaTimerCallbackData();
pd->p = p;
pd->type = 0;
pd->id = SDL_AddTimer(lua_tointeger(L, 3), LuaTimerCallbackGate, pd);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "tmcb%d", pd->id);
lua_pushvalue(L, 2);
lua_settable(L, -3);
return 0;
}
static int setInterval(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TFUNCTION, LUA_TNUMBER }); // ms
Window* p = (Window*)lua_touserdata(L, 1);
LuaTimerCallbackData* pd = new LuaTimerCallbackData();
pd->p = p;
pd->type = 1;
pd->id = SDL_AddTimer(lua_tointeger(L, 3), LuaTimerCallbackGate, pd);
lua_pushlightuserdata(L, p);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushfstring(L, "tmcb%d", pd->id);
lua_pushvalue(L, 2);
lua_settable(L, -3);
return 0;
}
static int showWindow(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Window* p = (Window*)lua_touserdata(L, 1);
SDL_ShowWindow(p->wnd);
return 0;
}
static int hideWindow(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Window* p = (Window*)lua_touserdata(L, 1);
SDL_HideWindow(p->wnd);
return 0;
}
static int create(lua_State* L)
{
check(L, { LUA_TSTRING, LUA_TNUMBER, LUA_TNUMBER });
cout << "In Window::create" << endl;
Window* p = (Window*) lua_newuserdata(L, sizeof(Window));
// Data table (for storing lua values)
lua_pushlightuserdata(L, p);
lua_newtable(L);
lua_settable(L, LUA_REGISTRYINDEX);
// Metatable
lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "window"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
setfn(L, set_event_handler, "on");
setfn(L, start, "start");
setfn(L, setTimeout, "setTimeout");
setfn(L, setInterval, "setInterval");
setfn(L, showWindow, "show");
setfn(L, hideWindow, "hide");
// Binding
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__window_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__window_mt");
}
lua_setmetatable(L, -2);
// Internal logic
p->wnd = SDL_CreateWindow(lua_tostring(L, 1), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, lua_tointeger(L, 2), lua_tointeger(L, 3), SDL_WINDOW_HIDDEN);
return 1;
}
};
struct Texture
{
SDL_Texture* text;
int w;
int h;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Texture::close" << endl;
Texture* t = (Texture*)lua_touserdata(L, 1);
if (t->text)
{
SDL_DestroyTexture(t->text);
t->text = nullptr;
}
return 0;
}
static void create(lua_State* L) // -2, +1
{
// <20>˺<EFBFBD><CBBA><EFBFBD><EFBFBD><EFBFBD>Renderer<65><72>C<EFBFBD><43>ֱ<EFBFBD>ӵ<EFBFBD><D3B5><EFBFBD>. <20><><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0>Ҫ<EFBFBD>ȷ<EFBFBD>һ<EFBFBD><D2BB>renderer userdata<74><61>lightuserdata<74><61>ջ<EFBFBD><D5BB>, ֵ<><D6B5><EFBFBD>ǵ<EFBFBD>ǰ<EFBFBD><C7B0>SDL_Texture.
// <20><><EFBFBD>ú<EFBFBD>ջ<EFBFBD>ϻ<EFBFBD><CFBB><EFBFBD><EFBFBD><EFBFBD>Texture<72><65>userdata.
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Lua<75><61>û<EFBFBD><C3BB>ֱ<EFBFBD>Ӵ<EFBFBD><D3B4><EFBFBD>Texture<72>ķ<EFBFBD><C4B7><EFBFBD>.
cout << "In Texture::create" << endl;
Texture* p = (Texture*)lua_newuserdata(L, sizeof(Texture));
// Metatable
lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "texture"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
// Binding
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__texture_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__texture_mt");
}
lua_setmetatable(L, -2);
p->text = (SDL_Texture * )lua_touserdata(L, -2);
SDL_QueryTexture(p->text, NULL, NULL, &p->w, &p->h);
lua_pushvalue(L, -3);
lua_setuservalue(L, -2); // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Rendere userdata
// stack balance
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>lightuserdata
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>rendere userdata
}
};
int LuaRectPointToRect(lua_State* L, int index, SDL_Rect& rect)
{
if (lua_getfield(L, index, "type") != LUA_TSTRING)
{
lua_pop(L, 1);
return -1;
}
const char* type = lua_tostring(L, -1);
if (strcmp(type, "point") == 0)
{
lua_pop(L, 1);
lua_getfield(L, index, "x");
rect.x = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
rect.y = lua_tointeger(L, -1);
lua_pop(L, 1);
return 1;
}
else if (strcmp(type, "rect") == 0)
{
lua_pop(L, 1);
lua_getfield(L, index, "x");
rect.x = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
rect.y = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "w");
rect.w = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "h");
rect.h = lua_tointeger(L, -1);
lua_pop(L, 1);
return 0;
}
else
{
return -1;
}
}
struct Surface
{
SDL_Surface* surf;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Surface::close" << endl;
Surface* p = (Surface*)lua_touserdata(L, 1);
if (p->surf)
{
SDL_FreeSurface(p->surf);
p->surf = nullptr;
}
return 0;
}
static void create(lua_State* L) // -1, +1
{
cout << "In Surface::create" << endl;
Surface* p = (Surface*)lua_newuserdata(L, sizeof(Surface));
// Metatable
lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "surface"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
// Binding
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__surface_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__surface_mt");
}
lua_setmetatable(L, -2);
p->surf = (SDL_Surface*)lua_touserdata(L, -2);
// stack balance
lua_remove(L, lua_gettop(L) - 1); // ɾ<><C9BE>lightuserdata
}
};
struct Renderer
{
SDL_Renderer* rnd;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Renderer::close" << endl;
Renderer* p = (Renderer*)lua_touserdata(L, 1);
if (p->rnd)
{
SDL_DestroyRenderer(p->rnd);
p->rnd = nullptr;
}
return 0;
}
static int update(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Renderer* p = (Renderer*)lua_touserdata(L, 1);
SDL_RenderPresent(p->rnd);
return 0;
}
static int clear(lua_State* L)
{
check(L, { LUA_TUSERDATA });
Renderer* p = (Renderer*)lua_touserdata(L, 1);
SDL_RenderClear(p->rnd);
return 0;
}
static int loadTexture(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TSTRING });
Renderer* p = (Renderer*)lua_touserdata(L, 1);
const char* filepath = lua_tostring(L, 2);
SDL_Texture* text = IMG_LoadTexture(p->rnd, filepath);
lua_pushvalue(L, 1);
lua_pushlightuserdata(L, text);
Texture::create(L);
return 1;
}
static int copy(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE, LUA_TTABLE });
Renderer* r = (Renderer*)lua_touserdata(L, 1);
Texture* t = (Texture*)lua_touserdata(L, 2);
SDL_Rect src;
int ret = LuaRectPointToRect(L, 3, src);
if (ret <= 0)
{
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
}
SDL_Rect dst;
ret = LuaRectPointToRect(L, 4, dst);
if (ret <= 0)
{
luaL_error(L, "Bad argument #%d. Rect expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
}
SDL_RenderCopy(r->rnd, t->text, &src, &dst);
return 0;
}
static int copyTo(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE });
Renderer* r = (Renderer*)lua_touserdata(L, 1);
Texture* t = (Texture*)lua_touserdata(L, 2);
SDL_Rect rect;
int ret = LuaRectPointToRect(L, 3, rect);
if (ret < 0)
{
luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
}
if (ret == 1)
{
rect.w = t->w;
rect.h = t->h;
}
SDL_RenderCopy(r->rnd, t->text, NULL, &rect);
return 0;
}
static int copyFill(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TTABLE });
Renderer* r = (Renderer*)lua_touserdata(L, 1);
Texture* t = (Texture*)lua_touserdata(L, 2);
SDL_Rect rect;
int ret = LuaRectPointToRect(L, 3, rect);
if (ret < 0)
{
luaL_error(L, "Bad argument #%d. Point or Rect expected, got %s", 3, lua_typename(L, lua_type(L, 3)));
}
if (ret == 1)
{
rect.w = t->w;
rect.h = t->h;
}
SDL_RenderCopy(r->rnd, t->text, &rect, NULL);
return 0;
}
static int create(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Renderer::create" << endl;
Renderer* p = (Renderer*)lua_newuserdata(L, sizeof(Renderer));
lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "renderer"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
setfn(L, update, "update");
setfn(L, clear, "clear");
setfn(L, loadTexture, "loadTexture");
setfn(L, copy, "copy");
setfn(L, copyTo, "copyTo");
// Set __index of metatable.
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__renderer_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__renderer_mt");
}
lua_setmetatable(L, -2);
// <20>󶨵<EFBFBD>ǰRenderer<65>Ķ<EFBFBD><C4B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><>ֹSDL_Window<6F><77><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD>.
lua_pushvalue(L, 1);
lua_setuservalue(L, -2);
// Internal logic
p->rnd = SDL_CreateRenderer(static_cast<Window*>(lua_touserdata(L, 1))->wnd, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE);
return 1;
}
};
int LuaColorToColor(lua_State* L, int index, SDL_Color& c)
{
if (lua_getfield(L, index, "type") != LUA_TSTRING)
{
lua_pop(L, 1);
return -1;
}
const char* type = lua_tostring(L, -1);
if (strcmp(type, "color") == 0)
{
lua_pop(L, 1);
lua_getfield(L, index, "r");
c.r = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "g");
c.g = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "b");
c.b = lua_tointeger(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "a");
c.a = lua_tointeger(L, -1);
lua_pop(L, 1);
return 0;
}
else
{
return -1;
}
}
struct Font
{
TTF_Font* font;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Font::close" << endl;
Font* f = (Font*)lua_touserdata(L, 1);
if (f->font)
{
TTF_CloseFont(f->font);
f->font = nullptr;
}
return 0;
}
static int renderText(lua_State* L)
{
Renderer* r = nullptr;
Font* f = nullptr;
const char* text = nullptr;
SDL_Color c;
if (lua_gettop(L) >= 4)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE });
f = (Font*)lua_touserdata(L, 1);
r = (Renderer*)lua_touserdata(L, 2);
text = lua_tostring(L, 3);
if (LuaColorToColor(L, 4, c) < 0)
{
luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
}
}
else
{
check(L, { LUA_TUSERDATA, LUA_TSTRING, LUA_TTABLE });
f = (Font*)lua_touserdata(L, 1);
text = lua_tostring(L, 3);
if (LuaColorToColor(L, 4, c) < 0)
{
luaL_error(L, "Bad argument #%d. Color expected, got %s", 4, lua_typename(L, lua_type(L, 4)));
}
}
SDL_Surface* surf = TTF_RenderText_Blended(f->font, text, c);
if (r)
{
SDL_Texture* text = SDL_CreateTextureFromSurface(r->rnd, surf);
SDL_FreeSurface(surf);
lua_pushvalue(L, 2);
lua_pushlightuserdata(L, text);
Texture::create(L);
}
else
{
lua_pushlightuserdata(L, surf);
Surface::create(L);
}
return 1;
}
static int create(lua_State* L)
{
check(L, { LUA_TSTRING, LUA_TNUMBER });
cout << "In Font::create" << endl;
Font* f = (Font*)lua_newuserdata(L, sizeof(Font));
lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "font"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
setfn(L, renderText, "renderText");
// Set __index of metatable.
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__font_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__font_mt");
}
lua_setmetatable(L, -2);
const char* filename = lua_tostring(L, 1);
int fontsz = lua_tointeger(L, 2);
f->font = TTF_OpenFont(filename, fontsz);
return 1;
}
};
struct Chunk
{
Mix_Chunk* chunk;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Chunk::close" << endl;
Chunk* c = (Chunk*)lua_touserdata(L, 1);
if (c->chunk)
{
Mix_FreeChunk(c->chunk);
c->chunk = nullptr;
}
return 0;
}
static int setVolume(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
Chunk* c = (Chunk*)lua_touserdata(L, 1);
Mix_VolumeChunk(c->chunk, lua_tointeger(L, 2));
return 0;
}
static int create(lua_State* L)
{
check(L, { LUA_TSTRING });
cout << "In Chunk::create" << endl;
Chunk* c = (Chunk*)lua_newuserdata(L, sizeof(Chunk));
lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "chunk"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
setfn(L, setVolume, "setVolumne");
// Set __index of metatable.
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__chunk_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__chunk_mt");
}
lua_setmetatable(L, -2);
const char* filename = lua_tostring(L, 1);
cout << "LoadWAV: " << filename << endl;
c->chunk = Mix_LoadWAV(filename);
cout << "Load chunk error: " << SDL_GetError() << endl;
return 1;
}
};
struct Music
{
Mix_Music* music;
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In Music::close" << endl;
Music* m = (Music*)lua_touserdata(L, 1);
if (m->music)
{
Mix_FreeMusic(m->music);
m->music = nullptr;
}
return 0;
}
static int create(lua_State* L)
{
check(L, { LUA_TSTRING });
cout << "In Music::create" << endl;
Music* m = (Music*)lua_newuserdata(L, sizeof(Music));
lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "music"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
// Set __index of metatable.
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__music_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__music_mt");
}
lua_setmetatable(L, -2);
const char* filename = lua_tostring(L, 1);
m->music = Mix_LoadMUS(filename);
cout << "Load music error: " << SDL_GetError() << endl;
return 1;
}
};
struct MusicPlayer
{
static int close(lua_State* L)
{
check(L, { LUA_TUSERDATA });
cout << "In MusicPlayer::close" << endl;
return 0;
}
static int playMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Music* m = (Music*)lua_touserdata(L, 2);
cout << "Play music ret: " << Mix_PlayMusic(m->music, 1) << " " << SDL_GetError() << endl;
return 0;
}
static int resumeMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_ResumeMusic();
return 0;
}
static int stopMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_HaltMusic();
return 0;
}
static int pauseMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_PauseMusic();
return 0;
}
static int rewindMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_RewindMusic();
return 0;
}
static int fadeInMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Music* m = (Music*)lua_touserdata(L, 2);
cout << "Fade in music ret: " << Mix_FadeInMusic(m->music, 1, lua_tointeger(L, 3)) << " " << SDL_GetError() << endl;
return 0;
}
static int fadeInMusicPos(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA , LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Music* m = (Music*)lua_touserdata(L, 2);
cout << "Fade in music ret: " << Mix_FadeInMusicPos(m->music, 1, lua_tointeger(L, 3), lua_tonumber(L, 4)) << " " << SDL_GetError() << endl;
return 0;
}
static int fadeOutMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
cout << "Fade out music ret: " << Mix_FadeOutMusic(lua_tointeger(L, 2)) << " " << SDL_GetError() << endl;
return 0;
}
static int setMusicVolume(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_VolumeMusic(lua_tointeger(L, 2));
return 0;
}
static int setMusicPos(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_SetMusicPosition(lua_tonumber(L, 2));
return 0;
}
static int isPlayingMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
lua_pushboolean(L, Mix_PlayingMusic());
return 1;
}
static int isPausedMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
lua_pushboolean(L, Mix_PausedMusic());
return 1;
}
static int isFadingMusic(lua_State* L)
{
check(L, { LUA_TUSERDATA });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
int ret = 0;
switch (Mix_FadingMusic())
{
case MIX_NO_FADING:
ret = 0;
break;
case MIX_FADING_OUT:
ret = -1;
break;
case MIX_FADING_IN:
return 1;
}
lua_pushinteger(L, ret);
return 1;
}
static int playChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER});
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Chunk* c = (Chunk*)lua_touserdata(L, 2);
cout << "Play channel ret: " << Mix_PlayChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4)) << " " << SDL_GetError() << endl;
return 0;
}
static int fadeInChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Chunk* c = (Chunk*)lua_touserdata(L, 2);
Mix_FadeInChannel(lua_tointeger(L, 3), c->chunk, lua_tointeger(L, 4), lua_tointeger(L, 5));
return 0;
}
static int fadeOutChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_FadeOutChannel(lua_tointeger(L, 2), lua_tointeger(L, 3));
return 0;
}
static int stopChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_HaltChannel(lua_tointeger(L, 2));
return 0;
}
static int resumeChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_Resume(lua_tointeger(L, 2));
return 0;
}
static int pauseChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_Pause(lua_tointeger(L, 2));
return 0;
}
static int isPlayingChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
lua_pushinteger(L, Mix_Playing(lua_tointeger(L, 2)));
return 1;
}
static int isPausedChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
lua_pushinteger(L, Mix_Paused(lua_tointeger(L, 2)));
return 1;
}
static int isFadingChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
int ret = 0;
switch (Mix_FadingChannel(lua_tointeger(L, 2)))
{
case MIX_NO_FADING:
ret = 0;
break;
case MIX_FADING_OUT:
ret = -1;
break;
case MIX_FADING_IN:
return 1;
}
lua_pushinteger(L, ret);
return 1;
}
static int setChannelVolume(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_Volume(lua_tointeger(L, 2), lua_tointeger(L, 3));
return 0;
}
static int setChannelDistance(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_SetDistance(lua_tointeger(L, 2), lua_tointeger(L, 3));
return 0;
}
static int setChannelPosition(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_SetPosition(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4));
return 0;
}
static int setChannelPanning(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER, LUA_TNUMBER, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_SetPanning(lua_tointeger(L, 2), lua_tointeger(L, 3), lua_tointeger(L, 4));
return 0;
}
static int setTotalChannel(lua_State* L)
{
check(L, { LUA_TUSERDATA, LUA_TNUMBER });
MusicPlayer* p = (MusicPlayer*)lua_touserdata(L, 1);
Mix_AllocateChannels(lua_tointeger(L, 2));
return 0;
}
static int create(lua_State* L)
{
cout << "In MusicPlayer::create" << endl;
MusicPlayer* m = (MusicPlayer*)lua_newuserdata(L, sizeof(MusicPlayer));
lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt");
if (lua_type(L, -1) == LUA_TNIL)
{
lua_pop(L, 1);
lua_newtable(L);
// GC
lua_pushcfunction(L, close);
lua_setfield(L, -2, "__gc");
// Fields
lua_newtable(L);
lua_pushstring(L, "musicplayer"); lua_setfield(L, -2, "type");
setfn(L, close, "close");
setfn(L, playMusic, "playMusic");
setfn(L, resumeMusic, "resumeMusic");
setfn(L, pauseMusic, "pauseMusic");
setfn(L, stopMusic, "stopMusic");
setfn(L, rewindMusic, "rewindMusic");
setfn(L, fadeInMusic, "fadeInMusic");
setfn(L, fadeOutMusic, "fadeOutMusic");
setfn(L, fadeInMusicPos, "fadeInMusicPos");
setfn(L, setMusicVolume, "setMusicVolume");
setfn(L, setMusicPos, "setMusicPos");
setfn(L, isPlayingMusic, "isPlayingMusic");
setfn(L, isPausedMusic, "isPausedMusic");
setfn(L, isFadingMusic, "isFadingMusic");
setfn(L, playChannel, "playChannel");
setfn(L, resumeChannel, "resumeChannel");
setfn(L, pauseChannel, "pauseChannel");
setfn(L, stopChannel, "stopChannel");
setfn(L, fadeInChannel, "fadeInChannel");
setfn(L, fadeOutChannel, "fadeOutChannel");
setfn(L, setChannelVolume, "setChannelVolume");
setfn(L, setChannelDistance, "setChannelDistance");
setfn(L, setChannelPosition, "setChannelPosition");
setfn(L, setChannelPanning, "setChannelPanning");
setfn(L, setTotalChannel, "setTotalChannel");
setfn(L, isPlayingChannel, "isPlayingChannel");
setfn(L, isPausedChannel, "isPausedChannel");
setfn(L, isFadingChannel, "isFadingChannel");
// Set __index of metatable.
lua_setfield(L, -2, "__index");
lua_setfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt");
lua_getfield(L, LUA_REGISTRYINDEX, "__musicplayer_mt");
}
lua_setmetatable(L, -2);
return 1;
}
};
struct LibFS
{
static int listdir(lua_State* L)
{
check(L, { LUA_TSTRING });
vector<FileInfo> vec = ListDir(lua_tostring(L, 1));
int sz = vec.size();
lua_newtable(L);
for (lua_Integer i = 0; i < sz; i++)
{
const FileInfo& info = vec[i];
lua_newtable(L);
lua_pushstring(L, info.name.c_str());
lua_setfield(L, -2, "name");
lua_pushstring(L, info.type ? "dir" : "file");
lua_setfield(L, -2, "type");
lua_pushinteger(L, info.size);
lua_setfield(L, -2, "size");
lua_seti(L, -2, i + 1);
}
return 1;
}
static int create(lua_State* L)
{
lua_newtable(L);
setfn(L, listdir, "listdir");
return 1;
}
};
#define reg_in_lua(L, cfunc, name) lua_pushcfunction(L, cfunc); lua_setglobal(L, name)
void check_init(lua_State* L, const char* name, int ret, int expected = 0)
{
if (ret != expected)
{
luaL_error(L, "Failed to initialize %s. ret: %d. expected: %d. SDLError: %s", name, ret, expected, SDL_GetError());
}
}
void _Global_On_Music_Finished()
{
SDL_Event e;
e.type = SDL_USEREVENT;
e.user.code = 1003;
SDL_PushEvent(&e);
}
void _Global_On_Channel_Finished(int channel)
{
SDL_Event e;
e.type = SDL_USEREVENT;
e.user.code = 1004;
e.user.data1 = new int(channel);
SDL_PushEvent(&e);
}
int InitEngine(lua_State* L)
{
int ret;
ret = SDL_Init(SDL_INIT_EVERYTHING);
check_init(L, "SDL", ret);
ret = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG);
check_init(L, "IMG", ret, IMG_INIT_JPG | IMG_INIT_PNG);
ret = TTF_Init();
check_init(L, "TTF", ret);
ret = Mix_Init(MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG );
check_init(L, "MIX", ret, MIX_INIT_MP3 | MIX_INIT_MOD | MIX_INIT_OGG);
ret = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 512);
check_init(L, "AUDIO", ret);
Mix_AllocateChannels(16);
Mix_HookMusicFinished(_Global_On_Music_Finished);
Mix_ChannelFinished(_Global_On_Channel_Finished);
reg_in_lua(L, Window::create, "Window");
reg_in_lua(L, Renderer::create, "Renderer");
reg_in_lua(L, Font::create, "Font");
reg_in_lua(L, Chunk::create, "Chunk");
reg_in_lua(L, Music::create, "Music");
reg_in_lua(L, MusicPlayer::create, "MusicPlayer");
reg_in_lua(L, ConvertMusicAsync, "ConvertMusic");
LibFS::create(L); lua_setglobal(L, "fs");
return 0;
}
int StopEngine()
{
Mix_CloseAudio();
Mix_Quit();
TTF_Quit();
IMG_Quit();
SDL_Quit();
return 0;
}