Files
basic1/emulator/main.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

139 lines
5.2 KiB
C++

// Entry point for the desktop emulator
#include "low_level_display_sfml.h"
#include "../display/low_level_render.h"
#include "../display/low_level_gui.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>
#include <iostream>
#include <vector>
#include <cstring>
constexpr int WIDTH = 400;
constexpr int HEIGHT = 300;
int main() {
// Allocate 1-bit framebuffer (rounded up)
size_t buffer_size = (WIDTH * HEIGHT + 7) / 8;
std::vector<uint8_t> framebuffer(buffer_size, 0);
// Set up display, renderer, and GUI
LowLevelDisplaySFML display(WIDTH, HEIGHT);
if (!display.init()) {
std::cerr << "Failed to initialize SFML display" << std::endl;
return 1;
}
LowLevelRenderer renderer(framebuffer.data(), WIDTH, HEIGHT);
LowLevelGUI gui(&renderer, font_5x5_obj);
InputManager input_manager; // Emulator stub
// 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);
});
launcher.register_game("Monopoly", "Classic property trading game",
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
return new MonopolyGame(w, h, r, g, im);
});
launcher.register_game("Demo Game", "Simple test game",
[](uint16_t w, uint16_t h, LowLevelRenderer* r, LowLevelGUI* g, InputManager* im) -> Game* {
return new DemoGame(w, h, r, g, im);
});
Game* current_game = nullptr;
bool running = true;
bool needs_redraw = true;
while (display.isOpen() && running) {
// Handle SFML events and translate to InputEvent
InputEvent event = {INPUT_NONE, 0, 0, 0, 0, 0, false};
while (const auto sfEvent = display.pollEvent()) {
if (const auto* closed = sfEvent->getIf<sf::Event::Closed>()) {
display.close();
running = false;
} else if (const auto* mousePressed = sfEvent->getIf<sf::Event::MouseButtonPressed>()) {
event.type = INPUT_TOUCH_DOWN;
event.x = mousePressed->position.x;
event.y = mousePressed->position.y;
event.valid = true;
// Check for virtual buttons
InputType virtual_type;
if (input_manager.check_virtual_buttons(event.x, event.y, virtual_type)) {
event.type = virtual_type;
}
} 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 (keyPressed->code == sf::Keyboard::Key::Enter) {
event.type = INPUT_BUTTON_1;
event.valid = true;
} else if (keyPressed->code == sf::Keyboard::Key::Escape) {
// Simulate long-press exit
if (launcher.is_game_selected()) {
launcher.reset();
needs_redraw = true;
}
}
}
}
if (event.valid) {
if (launcher.is_game_selected()) {
current_game = launcher.get_selected_game();
needs_redraw = current_game->update(event) || needs_redraw;
if (current_game->wants_to_exit()) {
launcher.reset();
needs_redraw = true;
}
} else {
bool game_selected = launcher.update(event);
if (game_selected) {
needs_redraw = true;
}
}
} else if (launcher.is_game_selected()) {
// No user input, but check if game wants frame tick updates
current_game = launcher.get_selected_game();
if (current_game->wants_frame_updates()) {
InputEvent frame_event = {INPUT_FRAME_TICK, 0, 0, 0, 0, 0, true};
needs_redraw = current_game->update(frame_event) || needs_redraw;
if (current_game->wants_to_exit()) {
launcher.reset();
needs_redraw = true;
}
}
}
// 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;
}