Add Lua scripting support to desktop emulator
- Created emulator-specific lua_game_emulator.cpp using filesystem instead of FatFS - Created lua_game_loader_emulator.cpp to scan games/lua_examples directory - Updated CMakeLists.txt to include Lua 5.4 engine and bindings - Updated to SFML 3.0 API compatibility (event handling, sprite initialization) - Updated Game class to use public members for Lua bindings - Updated GameLauncher to use std::function for lambda captures - Added continuous 60 FPS rendering for smooth display - Emulator now loads and runs all three example Lua games
This commit is contained in:
@@ -3,17 +3,41 @@ project(basic1_emulator)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
find_package(SFML 2.5 COMPONENTS graphics window system REQUIRED)
|
||||
find_package(SFML 3.0 COMPONENTS Graphics Window System REQUIRED)
|
||||
|
||||
# Lua source files
|
||||
file(GLOB LUA_SOURCES "../lib/lua/*.c")
|
||||
list(FILTER LUA_SOURCES EXCLUDE REGEX "lua\\.c$|luac\\.c$|loslib\\.c$|liolib\\.c$")
|
||||
|
||||
# Game source files
|
||||
set(GAME_SOURCES
|
||||
../games/lua_bindings.cpp
|
||||
../games/demo_game.cpp
|
||||
../games/tic_tac_toe.cpp
|
||||
../games/monopoly/monopoly_game.cpp
|
||||
../games/monopoly/player.c
|
||||
../lib/game_launcher.cpp
|
||||
../display/low_level_render.cpp
|
||||
../display/low_level_gui.cpp
|
||||
)
|
||||
|
||||
# Add source files
|
||||
set(SOURCES
|
||||
main.cpp
|
||||
low_level_display_sfml.cpp
|
||||
lua_game_emulator.cpp
|
||||
lua_game_loader_emulator.cpp
|
||||
input_manager.cpp
|
||||
${GAME_SOURCES}
|
||||
${LUA_SOURCES}
|
||||
# Add more emulator-specific sources here
|
||||
)
|
||||
|
||||
add_executable(basic1_emulator ${SOURCES})
|
||||
|
||||
target_include_directories(basic1_emulator PRIVATE ../display ../fonts ../games .)
|
||||
# Define LUA_32BITS for 32-bit embedded mode
|
||||
target_compile_definitions(basic1_emulator PRIVATE LUA_32BITS=1)
|
||||
|
||||
target_link_libraries(basic1_emulator sfml-graphics sfml-window sfml-system)
|
||||
target_include_directories(basic1_emulator PRIVATE . .. ../display ../fonts ../games ../lib ../lib/lua)
|
||||
|
||||
target_link_libraries(basic1_emulator SFML::Graphics SFML::Window SFML::System)
|
||||
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
virtual bool update(const InputEvent& event) = 0;
|
||||
virtual void draw() = 0;
|
||||
virtual bool wants_to_exit() const { return false; }
|
||||
protected:
|
||||
|
||||
// Public members for Lua bindings access
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
LowLevelRenderer* renderer;
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
// Copy of game_launcher.h for emulator build
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include "input_event.h"
|
||||
#include "game.h"
|
||||
|
||||
class LowLevelRenderer;
|
||||
class LowLevelGUI;
|
||||
class InputManager;
|
||||
|
||||
struct GameEntry {
|
||||
const char* name;
|
||||
const char* description;
|
||||
Game* (*factory)(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
|
||||
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory;
|
||||
};
|
||||
|
||||
class GameLauncher {
|
||||
public:
|
||||
GameLauncher(uint16_t width, uint16_t height, LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager);
|
||||
void register_game(const char* name, const char* description, Game* (*factory)(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*));
|
||||
|
||||
void register_game(const char* name, const char* description,
|
||||
std::function<Game*(uint16_t, uint16_t, LowLevelRenderer*, LowLevelGUI*, InputManager*)> factory);
|
||||
|
||||
void draw();
|
||||
bool update(const InputEvent& event);
|
||||
Game* get_selected_game();
|
||||
void reset();
|
||||
bool is_game_selected() const { return selected_game != nullptr; }
|
||||
|
||||
private:
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
@@ -29,6 +37,7 @@ private:
|
||||
std::vector<GameEntry> games;
|
||||
int selected_index;
|
||||
Game* selected_game;
|
||||
|
||||
static const int MENU_Y_START = 60;
|
||||
static const int MENU_ITEM_HEIGHT = 40;
|
||||
static const int MENU_PADDING = 10;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Emulator stub for InputManager implementation
|
||||
#include "input_manager.h"
|
||||
// No implementation needed for stub
|
||||
|
||||
// Methods are all defined inline in the header
|
||||
// This file exists just to ensure the class has a compilation unit
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
// Minimal stub for emulator build
|
||||
class InputManager {
|
||||
public:
|
||||
bool has_buttons() const { return false; }
|
||||
bool has_touch() const { return false; }
|
||||
inline bool has_buttons() const { return false; }
|
||||
inline bool has_touch() const { return false; }
|
||||
|
||||
void get_virtual_button_regions(int* a_rect, int* b_rect) const {
|
||||
inline void get_virtual_button_regions(int* a_rect, int* b_rect) const {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
a_rect[i] = v_button_a[i];
|
||||
b_rect[i] = v_button_b[i];
|
||||
}
|
||||
}
|
||||
void set_virtual_button_regions(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
|
||||
inline void set_virtual_button_regions(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh) {
|
||||
v_button_a[0] = ax; v_button_a[1] = ay; v_button_a[2] = aw; v_button_a[3] = ah;
|
||||
v_button_b[0] = bx; v_button_b[1] = by; v_button_b[2] = bw; v_button_b[3] = bh;
|
||||
v_buttons_active = true;
|
||||
}
|
||||
void clear_virtual_button_regions() {
|
||||
inline void clear_virtual_button_regions() {
|
||||
v_buttons_active = false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
v_button_a[i] = 0;
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool check_virtual_buttons(int16_t x, int16_t y, InputType& out_type) const {
|
||||
inline bool check_virtual_buttons(int16_t x, int16_t y, InputType& out_type) const {
|
||||
if (!v_buttons_active) return false;
|
||||
|
||||
if (x >= v_button_a[0] && x <= v_button_a[0] + v_button_a[2] &&
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
#include <vector>
|
||||
|
||||
// Add missing method implementations for emulator linkage
|
||||
bool LowLevelDisplaySFML::pollEvent(sf::Event& event) {
|
||||
return window.pollEvent(event);
|
||||
std::optional<sf::Event> LowLevelDisplaySFML::pollEvent() {
|
||||
return window.pollEvent();
|
||||
}
|
||||
|
||||
void LowLevelDisplaySFML::close() {
|
||||
@@ -14,18 +14,22 @@ void LowLevelDisplaySFML::close() {
|
||||
}
|
||||
|
||||
LowLevelDisplaySFML::LowLevelDisplaySFML(int w, int h)
|
||||
: width(w), height(h), window(sf::VideoMode(w, h), "basic1 Emulator"), framebuffer((w * h + 7) / 8, 0) {}
|
||||
: width(w), height(h),
|
||||
window(sf::VideoMode({(unsigned)w, (unsigned)h}), "basic1 Emulator"),
|
||||
framebuffer((w * h + 7) / 8, 0) {}
|
||||
|
||||
bool LowLevelDisplaySFML::init() {
|
||||
texture.create(width, height);
|
||||
sprite.setTexture(texture);
|
||||
if (!texture.resize({(unsigned)width, (unsigned)height})) {
|
||||
return false;
|
||||
}
|
||||
sprite.emplace(texture);
|
||||
return window.isOpen();
|
||||
}
|
||||
|
||||
void LowLevelDisplaySFML::draw_buffer(const uint8_t* bit_buffer) {
|
||||
// Convert 1-bit buffer to 8-bit grayscale (or RGBA) for SFML
|
||||
// Each bit in bit_buffer represents a pixel (0=black, 1=white)
|
||||
std::vector<sf::Uint8> pixels(width * height * 4, 0);
|
||||
std::vector<std::uint8_t> pixels(width * height * 4, 0);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
int bit_index = y * width + x;
|
||||
@@ -33,7 +37,7 @@ void LowLevelDisplaySFML::draw_buffer(const uint8_t* bit_buffer) {
|
||||
int bit_offset = 7 - (bit_index % 8);
|
||||
bool on = (bit_buffer[byte_index] >> bit_offset) & 0x1;
|
||||
int idx = (y * width + x) * 4;
|
||||
sf::Uint8 color = on ? 255 : 0;
|
||||
std::uint8_t color = on ? 255 : 0;
|
||||
pixels[idx + 0] = color; // R
|
||||
pixels[idx + 1] = color; // G
|
||||
pixels[idx + 2] = color; // B
|
||||
@@ -44,14 +48,10 @@ void LowLevelDisplaySFML::draw_buffer(const uint8_t* bit_buffer) {
|
||||
}
|
||||
|
||||
void LowLevelDisplaySFML::refresh() {
|
||||
sf::Event event;
|
||||
while (window.pollEvent(event)) {
|
||||
if (event.type == sf::Event::Closed)
|
||||
window.close();
|
||||
// TODO: Handle mouse/keyboard input here
|
||||
}
|
||||
window.clear(sf::Color::Black);
|
||||
window.draw(sprite);
|
||||
if (sprite) {
|
||||
window.draw(*sprite);
|
||||
}
|
||||
window.display();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <optional>
|
||||
|
||||
class LowLevelDisplaySFML {
|
||||
public:
|
||||
@@ -8,12 +9,12 @@ public:
|
||||
void draw_buffer(const uint8_t* bit_buffer);
|
||||
void refresh();
|
||||
bool isOpen() const;
|
||||
bool pollEvent(sf::Event& event);
|
||||
std::optional<sf::Event> pollEvent();
|
||||
void close();
|
||||
private:
|
||||
int width, height;
|
||||
sf::RenderWindow window;
|
||||
sf::Texture texture;
|
||||
sf::Sprite sprite;
|
||||
std::optional<sf::Sprite> sprite;
|
||||
std::vector<uint8_t> framebuffer;
|
||||
};
|
||||
|
||||
193
emulator/lua_game_emulator.cpp
Normal file
193
emulator/lua_game_emulator.cpp
Normal file
@@ -0,0 +1,193 @@
|
||||
// ============================================================================
|
||||
// LUA GAME WRAPPER - EMULATOR IMPLEMENTATION
|
||||
// ============================================================================
|
||||
// Manages Lua VM lifecycle and script execution for desktop emulator
|
||||
|
||||
#include "../games/lua_game.h"
|
||||
#include "../games/lua_bindings.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
LuaGame::LuaGame(const char* script_path, uint16_t width, uint16_t height,
|
||||
LowLevelRenderer* renderer, LowLevelGUI* gui, InputManager* input_manager)
|
||||
: Game(width, height, renderer, gui, input_manager),
|
||||
L(nullptr),
|
||||
script_path(script_path),
|
||||
loaded(false) {
|
||||
|
||||
// Create new Lua state
|
||||
L = luaL_newstate();
|
||||
if (!L) {
|
||||
error_message = "Failed to create Lua state";
|
||||
printf("LuaGame: %s\n", error_message.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Open standard Lua libraries (math, string, table, coroutine)
|
||||
luaL_openlibs(L);
|
||||
|
||||
// Register game API bindings
|
||||
lua_bindings_register(L, this);
|
||||
|
||||
// Load the script
|
||||
loaded = load_script();
|
||||
|
||||
if (!loaded) {
|
||||
printf("LuaGame: Failed to load %s: %s\n", script_path, error_message.c_str());
|
||||
} else {
|
||||
printf("LuaGame: Successfully loaded %s\n", script_path);
|
||||
}
|
||||
}
|
||||
|
||||
LuaGame::~LuaGame() {
|
||||
if (L) {
|
||||
lua_close(L);
|
||||
L = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaGame::load_script() {
|
||||
// Open Lua script from filesystem (emulator)
|
||||
std::ifstream file(script_path);
|
||||
if (!file.is_open()) {
|
||||
error_message = "Failed to open file: " + script_path;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read entire file into string
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
std::string script_content = buffer.str();
|
||||
file.close();
|
||||
|
||||
if (script_content.empty()) {
|
||||
error_message = "Script file is empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (script_content.size() > 64 * 1024) { // Limit to 64KB
|
||||
error_message = "Script file too large (> 64KB)";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load script into Lua
|
||||
int result = luaL_loadbuffer(L, script_content.c_str(), script_content.size(), script_path.c_str());
|
||||
|
||||
if (result != LUA_OK) {
|
||||
report_error("load script");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Execute script (loads functions into global namespace)
|
||||
result = lua_pcall(L, 0, 0, 0);
|
||||
if (result != LUA_OK) {
|
||||
report_error("execute script");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaGame::init() {
|
||||
if (!loaded) return;
|
||||
|
||||
// Call Lua init() function if it exists
|
||||
lua_getglobal(L, "init");
|
||||
if (lua_isfunction(L, -1)) {
|
||||
call_lua_function("init", 0, 0);
|
||||
} else {
|
||||
lua_pop(L, 1); // Pop non-function value
|
||||
printf("LuaGame: Warning - no init() function found\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaGame::update(const InputEvent& event) {
|
||||
if (!loaded) return false;
|
||||
|
||||
// Call Lua update(event) function if it exists
|
||||
lua_getglobal(L, "update");
|
||||
if (!lua_isfunction(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return false; // No update function, no redraw needed
|
||||
}
|
||||
|
||||
// Push event table to Lua
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "type");
|
||||
lua_pushinteger(L, (int)event.type);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "x");
|
||||
lua_pushinteger(L, event.x);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "y");
|
||||
lua_pushinteger(L, event.y);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "button_id");
|
||||
lua_pushinteger(L, event.button_id);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "valid");
|
||||
lua_pushboolean(L, event.valid);
|
||||
lua_settable(L, -3);
|
||||
|
||||
// Call update(event) with 1 arg, expecting 1 result (needs_redraw)
|
||||
if (!call_lua_function("update", 1, 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get return value (needs redraw?)
|
||||
bool needs_redraw = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return needs_redraw;
|
||||
}
|
||||
|
||||
void LuaGame::draw() {
|
||||
if (!loaded) return;
|
||||
|
||||
// Call Lua draw() function if it exists
|
||||
lua_getglobal(L, "draw");
|
||||
if (lua_isfunction(L, -1)) {
|
||||
call_lua_function("draw", 0, 0);
|
||||
} else {
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuaGame::wants_to_exit() const {
|
||||
if (!L) return false;
|
||||
|
||||
// Check if Lua script requested exit
|
||||
lua_pushstring(L, "__exit_requested");
|
||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
||||
bool exit = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
bool LuaGame::call_lua_function(const char* func_name, int nargs, int nresults) {
|
||||
int result = lua_pcall(L, nargs, nresults, 0);
|
||||
if (result != LUA_OK) {
|
||||
report_error(func_name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LuaGame::report_error(const char* context) {
|
||||
const char* msg = lua_tostring(L, -1);
|
||||
if (msg) {
|
||||
error_message = context;
|
||||
error_message += ": ";
|
||||
error_message += msg;
|
||||
printf("LuaGame Error [%s]: %s\n", context, msg);
|
||||
}
|
||||
lua_pop(L, 1); // Pop error message
|
||||
}
|
||||
159
emulator/lua_game_loader_emulator.cpp
Normal file
159
emulator/lua_game_loader_emulator.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
// ============================================================================
|
||||
// LUA GAME LOADER - EMULATOR IMPLEMENTATION
|
||||
// ============================================================================
|
||||
// Discovers Lua scripts from filesystem and integrates with game launcher
|
||||
|
||||
#include "../games/lua_game_loader.h"
|
||||
#include "../games/lua_game.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// Structure to hold script path for factory closure
|
||||
struct LuaGameFactoryData {
|
||||
char script_path[256];
|
||||
};
|
||||
|
||||
static std::vector<LuaGameFactoryData*> factory_data_list;
|
||||
|
||||
bool LuaGameLoader::parse_metadata(const char* script_path, char* name, char* description) {
|
||||
// Default name from filename
|
||||
fs::path path(script_path);
|
||||
std::string filename = path.stem().string(); // Get filename without extension
|
||||
strncpy(name, filename.c_str(), 63);
|
||||
name[63] = '\0';
|
||||
|
||||
// Default empty description
|
||||
description[0] = '\0';
|
||||
|
||||
// Try to open file and parse metadata comments
|
||||
std::ifstream file(script_path);
|
||||
if (!file.is_open()) {
|
||||
printf("LuaGameLoader: Warning - could not open %s for metadata\n", script_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read first 512 bytes to look for metadata comments
|
||||
char buffer[512];
|
||||
file.read(buffer, sizeof(buffer) - 1);
|
||||
std::streamsize bytes_read = file.gcount();
|
||||
file.close();
|
||||
|
||||
if (bytes_read == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
// Parse metadata comments: -- NAME: Game Name
|
||||
char* line = buffer;
|
||||
while (line && (line - buffer) < bytes_read) {
|
||||
char* next_line = strchr(line, '\n');
|
||||
if (next_line) *next_line = '\0';
|
||||
|
||||
// Check for -- NAME:
|
||||
if (strncmp(line, "-- NAME:", 8) == 0) {
|
||||
const char* value = line + 8;
|
||||
while (*value == ' ') value++; // Skip spaces
|
||||
strncpy(name, value, 63);
|
||||
name[63] = '\0';
|
||||
}
|
||||
// Check for -- DESC:
|
||||
else if (strncmp(line, "-- DESC:", 8) == 0) {
|
||||
const char* value = line + 8;
|
||||
while (*value == ' ') value++;
|
||||
strncpy(description, value, 127);
|
||||
description[127] = '\0';
|
||||
}
|
||||
|
||||
if (next_line) {
|
||||
line = next_line + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int LuaGameLoader::register_all_games(GameLauncher* launcher) {
|
||||
int count = 0;
|
||||
|
||||
printf("LuaGameLoader: Scanning games/lua_examples directory for .lua scripts...\n");
|
||||
|
||||
// Path to lua examples relative to emulator binary
|
||||
const char* search_paths[] = {
|
||||
"../games/lua_examples",
|
||||
"games/lua_examples",
|
||||
"./lua_examples"
|
||||
};
|
||||
|
||||
fs::path games_dir;
|
||||
bool found_dir = false;
|
||||
|
||||
// Try to find the lua_examples directory
|
||||
for (const char* search_path : search_paths) {
|
||||
if (fs::exists(search_path) && fs::is_directory(search_path)) {
|
||||
games_dir = fs::path(search_path);
|
||||
found_dir = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_dir) {
|
||||
printf("LuaGameLoader: Could not find games/lua_examples directory\n");
|
||||
printf("LuaGameLoader: Tried: ../games/lua_examples, games/lua_examples, ./lua_examples\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("LuaGameLoader: Found directory: %s\n", games_dir.string().c_str());
|
||||
|
||||
// Scan for .lua files
|
||||
try {
|
||||
for (const auto& entry : fs::directory_iterator(games_dir)) {
|
||||
if (!entry.is_regular_file()) continue;
|
||||
|
||||
// Check for .lua extension
|
||||
if (entry.path().extension() != ".lua") continue;
|
||||
|
||||
std::string script_path = entry.path().string();
|
||||
|
||||
// Parse metadata
|
||||
char name[64];
|
||||
char description[128];
|
||||
parse_metadata(script_path.c_str(), name, description);
|
||||
|
||||
printf("LuaGameLoader: Found %s - '%s'\n", entry.path().filename().string().c_str(), name);
|
||||
|
||||
// Create factory data (persistent for game lifetime)
|
||||
LuaGameFactoryData* data = new LuaGameFactoryData();
|
||||
strncpy(data->script_path, script_path.c_str(), sizeof(data->script_path) - 1);
|
||||
data->script_path[sizeof(data->script_path) - 1] = '\0';
|
||||
factory_data_list.push_back(data);
|
||||
|
||||
// Register with launcher - using lambda factory pattern
|
||||
launcher->register_game(
|
||||
name,
|
||||
description[0] ? description : "Lua Script",
|
||||
[data](uint16_t width, uint16_t height, LowLevelRenderer* renderer,
|
||||
LowLevelGUI* gui, InputManager* input_manager) -> Game* {
|
||||
return new LuaGame(data->script_path, width, height, renderer, gui, input_manager);
|
||||
}
|
||||
);
|
||||
|
||||
count++;
|
||||
}
|
||||
} catch (const fs::filesystem_error& e) {
|
||||
printf("LuaGameLoader: Error scanning directory: %s\n", e.what());
|
||||
return count;
|
||||
}
|
||||
|
||||
printf("LuaGameLoader: Registered %d Lua games\n", count);
|
||||
return count;
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
#include "low_level_display_sfml.h"
|
||||
#include "../display/low_level_render.h"
|
||||
#include "../display/low_level_gui.h"
|
||||
#include "game_launcher.h"
|
||||
#include "../lib/game_launcher.h"
|
||||
#include "../games/demo_game.h"
|
||||
#include "../games/tic_tac_toe.h"
|
||||
#include "../games/monopoly/monopoly_game.h"
|
||||
#include "../games/lua_game_loader.h"
|
||||
#include "input_manager.h"
|
||||
#include <SFML/Window.hpp>
|
||||
#include <SFML/Graphics.hpp>
|
||||
@@ -34,6 +35,11 @@ int main() {
|
||||
|
||||
// Create GameLauncher
|
||||
GameLauncher launcher(WIDTH, HEIGHT, &renderer, &gui, &input_manager);
|
||||
|
||||
// Register Lua games from lua_examples directory
|
||||
LuaGameLoader::register_all_games(&launcher);
|
||||
|
||||
// Register built-in C++ games
|
||||
launcher.register_game("Tic-Tac-Toe", "Classic 2-player game",
|
||||
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
|
||||
return new TicTacToeGame(w, h, r, g, im);
|
||||
@@ -54,15 +60,15 @@ int main() {
|
||||
while (display.isOpen() && running) {
|
||||
// Handle SFML events and translate to InputEvent
|
||||
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
|
||||
sf::Event sfEvent;
|
||||
while (display.pollEvent(sfEvent)) {
|
||||
if (sfEvent.type == sf::Event::Closed) {
|
||||
|
||||
while (const auto sfEvent = display.pollEvent()) {
|
||||
if (const auto* closed = sfEvent->getIf<sf::Event::Closed>()) {
|
||||
display.close();
|
||||
running = false;
|
||||
} else if (sfEvent.type == sf::Event::MouseButtonPressed) {
|
||||
} else if (const auto* mousePressed = sfEvent->getIf<sf::Event::MouseButtonPressed>()) {
|
||||
event.type = INPUT_TOUCH_DOWN;
|
||||
event.x = sfEvent.mouseButton.x;
|
||||
event.y = sfEvent.mouseButton.y;
|
||||
event.x = mousePressed->position.x;
|
||||
event.y = mousePressed->position.y;
|
||||
event.valid = true;
|
||||
|
||||
// Check for virtual buttons
|
||||
@@ -70,14 +76,14 @@ int main() {
|
||||
if (input_manager.check_virtual_buttons(event.x, event.y, virtual_type)) {
|
||||
event.type = virtual_type;
|
||||
}
|
||||
} else if (sfEvent.type == sf::Event::KeyPressed) {
|
||||
if (sfEvent.key.code == sf::Keyboard::Space) {
|
||||
} else if (const auto* keyPressed = sfEvent->getIf<sf::Event::KeyPressed>()) {
|
||||
if (keyPressed->code == sf::Keyboard::Key::Space) {
|
||||
event.type = INPUT_BUTTON_0;
|
||||
event.valid = true;
|
||||
} else if (sfEvent.key.code == sf::Keyboard::Enter) {
|
||||
} else if (keyPressed->code == sf::Keyboard::Key::Enter) {
|
||||
event.type = INPUT_BUTTON_1;
|
||||
event.valid = true;
|
||||
} else if (sfEvent.key.code == sf::Keyboard::Escape) {
|
||||
} else if (keyPressed->code == sf::Keyboard::Key::Escape) {
|
||||
// Simulate long-press exit
|
||||
if (launcher.is_game_selected()) {
|
||||
launcher.reset();
|
||||
@@ -103,18 +109,19 @@ int main() {
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_redraw) {
|
||||
renderer.clear_buffer();
|
||||
if (launcher.is_game_selected()) {
|
||||
current_game = launcher.get_selected_game();
|
||||
current_game->draw();
|
||||
} else {
|
||||
launcher.draw();
|
||||
}
|
||||
display.draw_buffer(framebuffer.data());
|
||||
display.refresh();
|
||||
needs_redraw = false;
|
||||
// Always redraw every frame for emulator
|
||||
renderer.clear_buffer();
|
||||
if (launcher.is_game_selected()) {
|
||||
current_game = launcher.get_selected_game();
|
||||
current_game->draw();
|
||||
} else {
|
||||
launcher.draw();
|
||||
}
|
||||
display.draw_buffer(framebuffer.data());
|
||||
display.refresh();
|
||||
|
||||
// Small delay to prevent busy-waiting (60 FPS)
|
||||
sf::sleep(sf::milliseconds(16));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user