- Fix basic1.cpp loop to handle set_frame_updates(true) correctly with sleep - Update emulator loop for concurrent input and frame updates - Update emulator for SFML 3.0 compatibility - Add INPUT.FRAME_TICK constant to Lua bindings - Enable frame updates in snake.lua example
321 lines
8.6 KiB
C++
321 lines
8.6 KiB
C++
// ============================================================================
|
|
// LUA BINDINGS - IMPLEMENTATION
|
|
// ============================================================================
|
|
// Exposes C++ rendering and input APIs to Lua scripts
|
|
|
|
#include "lua_bindings.h"
|
|
#include "lua_game.h"
|
|
#include "../display/low_level_render.h"
|
|
#include "../display/low_level_gui.h"
|
|
#include "../lib/input_manager.h"
|
|
#include <stdio.h>
|
|
|
|
// Registry key for LuaGame pointer
|
|
static const char* LUAGAME_REGISTRY_KEY = "LUAGAME_PTR";
|
|
|
|
// Helper: Get LuaGame instance from registry
|
|
static LuaGame* get_game(lua_State* L) {
|
|
lua_pushstring(L, LUAGAME_REGISTRY_KEY);
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
LuaGame* game = (LuaGame*)lua_touserdata(L, -1);
|
|
lua_pop(L, 1);
|
|
return game;
|
|
}
|
|
|
|
// ============================================================================
|
|
// RENDERER BINDINGS
|
|
// ============================================================================
|
|
|
|
// renderer.clear(white)
|
|
static int lua_renderer_clear(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
bool white = lua_toboolean(L, 1);
|
|
game->renderer->clear_buffer();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.pixel(x, y, on)
|
|
static int lua_renderer_pixel(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x = luaL_checkinteger(L, 1);
|
|
int y = luaL_checkinteger(L, 2);
|
|
bool on = lua_toboolean(L, 3);
|
|
|
|
game->renderer->set_pixel(x, y, on);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.rect(x, y, w, h, on, filled)
|
|
static int lua_renderer_rect(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x = luaL_checkinteger(L, 1);
|
|
int y = luaL_checkinteger(L, 2);
|
|
int w = luaL_checkinteger(L, 3);
|
|
int h = luaL_checkinteger(L, 4);
|
|
bool on = lua_toboolean(L, 5);
|
|
bool filled = lua_isnone(L, 6) ? false : lua_toboolean(L, 6);
|
|
|
|
if (filled) {
|
|
game->renderer->draw_filled_rectangle(x, y, w, h, on, 1);
|
|
} else {
|
|
game->renderer->draw_rectangle(x, y, w, h, on, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.circle(x, y, radius, on, filled)
|
|
static int lua_renderer_circle(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x = luaL_checkinteger(L, 1);
|
|
int y = luaL_checkinteger(L, 2);
|
|
int radius = luaL_checkinteger(L, 3);
|
|
bool on = lua_toboolean(L, 4);
|
|
bool filled = lua_isnone(L, 5) ? false : lua_toboolean(L, 5);
|
|
|
|
if (filled) {
|
|
game->renderer->draw_filled_circle(x, y, radius, on);
|
|
} else {
|
|
game->renderer->draw_circle(x, y, radius, on);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.line(x0, y0, x1, y1, on, width)
|
|
static int lua_renderer_line(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x0 = luaL_checkinteger(L, 1);
|
|
int y0 = luaL_checkinteger(L, 2);
|
|
int x1 = luaL_checkinteger(L, 3);
|
|
int y1 = luaL_checkinteger(L, 4);
|
|
bool on = lua_toboolean(L, 5);
|
|
int width = lua_isnone(L, 6) ? 1 : luaL_checkinteger(L, 6);
|
|
|
|
game->renderer->draw_line(x0, y0, x1, y1, on, width);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.text(x, y, text, on)
|
|
static int lua_renderer_text(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x = luaL_checkinteger(L, 1);
|
|
int y = luaL_checkinteger(L, 2);
|
|
const char* text = luaL_checkstring(L, 3);
|
|
bool on = lua_toboolean(L, 4);
|
|
|
|
game->renderer->set_text_color(on);
|
|
game->renderer->draw_string(x, y, text, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// renderer.triangle(x0, y0, x1, y1, x2, y2, on, filled)
|
|
static int lua_renderer_triangle(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
int x0 = luaL_checkinteger(L, 1);
|
|
int y0 = luaL_checkinteger(L, 2);
|
|
int x1 = luaL_checkinteger(L, 3);
|
|
int y1 = luaL_checkinteger(L, 4);
|
|
int x2 = luaL_checkinteger(L, 5);
|
|
int y2 = luaL_checkinteger(L, 6);
|
|
bool on = lua_toboolean(L, 7);
|
|
bool filled = lua_isnone(L, 8) ? false : lua_toboolean(L, 8);
|
|
|
|
if (filled) {
|
|
game->renderer->draw_filled_triangle(x0, y0, x1, y1, x2, y2, on);
|
|
} else {
|
|
game->renderer->draw_triangle(x0, y0, x1, y1, x2, y2, on);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================================
|
|
// GAME STATE BINDINGS
|
|
// ============================================================================
|
|
|
|
// Global table to store persistent state variables
|
|
// game.vars[key] = value
|
|
|
|
// game.width() - get display width
|
|
static int lua_game_width(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
lua_pushinteger(L, game->width);
|
|
return 1;
|
|
}
|
|
|
|
// game.height() - get display height
|
|
static int lua_game_height(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
lua_pushinteger(L, game->height);
|
|
return 1;
|
|
}
|
|
|
|
// game.exit() - request exit to launcher
|
|
static int lua_game_exit(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
// Set exit flag (will be checked in wants_to_exit())
|
|
lua_pushstring(L, "__exit_requested");
|
|
lua_pushboolean(L, true);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// game.set_frame_updates(enabled) - enable/disable continuous frame tick events
|
|
static int lua_game_set_frame_updates(lua_State* L) {
|
|
LuaGame* game = get_game(L);
|
|
if (!game) return 0;
|
|
|
|
bool enabled = lua_toboolean(L, 1);
|
|
|
|
// Set frame updates flag (will be checked in wants_frame_updates())
|
|
lua_pushstring(L, "__wants_frame_updates");
|
|
lua_pushboolean(L, enabled);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================================
|
|
// INPUT TYPE CONSTANTS
|
|
// ============================================================================
|
|
|
|
// Set up input type constants as global table
|
|
static void register_input_constants(lua_State* L) {
|
|
lua_newtable(L);
|
|
|
|
lua_pushstring(L, "NONE");
|
|
lua_pushinteger(L, 0);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "TOUCH_DOWN");
|
|
lua_pushinteger(L, 1);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "TOUCH_MOVE");
|
|
lua_pushinteger(L, 2);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "TOUCH_UP");
|
|
lua_pushinteger(L, 3);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "BUTTON_0");
|
|
lua_pushinteger(L, 4);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "BUTTON_1");
|
|
lua_pushinteger(L, 5);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "GESTURE");
|
|
lua_pushinteger(L, 6);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "FRAME_TICK");
|
|
lua_pushinteger(L, 7);
|
|
lua_settable(L, -3);
|
|
|
|
lua_setglobal(L, "INPUT");
|
|
}
|
|
|
|
// ============================================================================
|
|
// MAIN REGISTRATION FUNCTION
|
|
// ============================================================================
|
|
|
|
void lua_bindings_register(lua_State* L, LuaGame* game) {
|
|
// Store game pointer in registry
|
|
lua_pushstring(L, LUAGAME_REGISTRY_KEY);
|
|
lua_pushlightuserdata(L, game);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
|
|
// Create renderer table
|
|
lua_newtable(L);
|
|
|
|
lua_pushstring(L, "clear");
|
|
lua_pushcfunction(L, lua_renderer_clear);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "pixel");
|
|
lua_pushcfunction(L, lua_renderer_pixel);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "rect");
|
|
lua_pushcfunction(L, lua_renderer_rect);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "circle");
|
|
lua_pushcfunction(L, lua_renderer_circle);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "line");
|
|
lua_pushcfunction(L, lua_renderer_line);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "text");
|
|
lua_pushcfunction(L, lua_renderer_text);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "triangle");
|
|
lua_pushcfunction(L, lua_renderer_triangle);
|
|
lua_settable(L, -3);
|
|
|
|
lua_setglobal(L, "renderer");
|
|
|
|
// Create game table
|
|
lua_newtable(L);
|
|
|
|
lua_pushstring(L, "width");
|
|
lua_pushcfunction(L, lua_game_width);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "height");
|
|
lua_pushcfunction(L, lua_game_height);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "exit");
|
|
lua_pushcfunction(L, lua_game_exit);
|
|
lua_settable(L, -3);
|
|
|
|
lua_pushstring(L, "set_frame_updates");
|
|
lua_pushcfunction(L, lua_game_set_frame_updates);
|
|
lua_settable(L, -3);
|
|
|
|
// Create empty vars table for persistent state
|
|
lua_pushstring(L, "vars");
|
|
lua_newtable(L);
|
|
lua_settable(L, -3);
|
|
|
|
lua_setglobal(L, "game");
|
|
|
|
// Register input type constants
|
|
register_input_constants(L);
|
|
|
|
printf("Lua bindings registered\n");
|
|
}
|