Files
basic1/games/lua_bindings.cpp
Adolfo Reyna 2a472fc29f Add frame tick system for continuous animation
- Added INPUT_FRAME_TICK event type to input_event.h
- Added wants_frame_updates() virtual method to Game base class
- Implemented frame tick logic in main loop (basic1.cpp and emulator/main.cpp)
- Added Lua bindings: game.set_frame_updates(bool) and INPUT.FRAME_TICK
- Updated LuaGame to support frame updates via registry flag
- Updated ball.lua to use continuous frame updates for smooth animation
- Both hardware and emulator now support continuous animation for physics/games
2026-02-07 13:20:10 -05:00

317 lines
8.5 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_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");
}