2026-03-24 13:40:09 +00:00

170 lines
7.4 KiB
Rust

use crate::model::Color;
use crate::peripherals::{Gfx, InputSignals, Pad, Touch};
/// PROMETEU "hardware lógico" (v0).
/// O Host alimenta INPUT SIGNALS e chama `step_frame()` em 60Hz.
pub struct Machine {
pub gfx: Gfx,
pub pad: Pad,
pub touch: Touch,
pub frame_index: u64,
}
impl Machine {
pub const W: usize = 320;
pub const H: usize = 180;
pub fn new() -> Self {
Self {
gfx: Gfx::new(Self::W, Self::H),
pad: Pad::default(),
touch: Touch::default(),
frame_index: 0,
}
}
/// "Contrato de frame" do PROMETEU (Template Method sem loop).
/// O Host controla tempo/event loop; o Core define a sequência do frame.
pub fn step_frame(&mut self, signals: &InputSignals) {
self.begin_frame(signals);
self.tick(); // futuro: executa cartucho/VM
self.gfx.render_all(); // A máquina executa o pipeline gráfico (Ação física)
self.end_frame(); // present/housekeeping
}
/// Início do frame: zera flags transitórias.
pub fn begin_frame(&mut self, signals: &InputSignals) {
self.frame_index += 1;
// Flags transitórias do TOUCH devem durar 1 frame.
self.touch.begin_frame(signals);
// Se você quiser input com pressed/released depois:
self.pad.begin_frame(signals);
}
/// Lógica do frame (demo hardcoded por enquanto).
pub fn tick(&mut self) {
// Limpa a tela com um azul escuro "estilo console"
self.gfx.clear(Color::BLACK);
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
if self.gfx.banks[0].is_none() {
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size8, 128, 128);
// Pinta o primeiro tile (ID 1) de Branco para teste
// Ele começa no pixel (8, 0) pois o ID 0 é o primeiro (0,0)
let tile_size = 8;
let start_x = 8; // ID 1 está na segunda posição da primeira linha
let start_y = 0;
for y in 0..tile_size {
for x in 0..tile_size {
let px = start_x + x;
let py = start_y + y;
let idx = py * 128 + px;
test_bank.pixels[idx] = Color::GREEN;
}
}
self.gfx.banks[0] = Some(test_bank);
}
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
if self.gfx.banks[1].is_none() {
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
// Pinta o primeiro tile (ID 1) de Branco para teste
// Ele começa no pixel (16, 0) pois o ID 0 é o primeiro (0,0)
let tile_size = 16;
let start_x = 16; // ID 1 está na segunda posição da primeira linha
let start_y = 0;
for y in 0..tile_size {
for x in 0..tile_size {
let px = start_x + x;
let py = start_y + y;
let idx = py * 128 + px;
test_bank.pixels[idx] = Color::RED;
}
}
self.gfx.banks[1] = Some(test_bank);
}
// Simula o carregamento de um banco se estiver vazio (apenas para teste)
if self.gfx.banks[2].is_none() {
let mut test_bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128);
// Pinta o primeiro tile (ID 1) de Branco para teste
// Ele começa no pixel (16, 0) pois o ID 0 é o primeiro (0,0)
let tile_size = 16;
let start_x = 16; // ID 1 está na segunda posição da primeira linha
let start_y = 0;
for y in 0..tile_size {
for x in 0..tile_size {
let px = start_x + x;
let py = start_y + y;
let idx = py * 128 + px;
test_bank.pixels[idx] = Color::INDIGO;
}
}
self.gfx.banks[2] = Some(test_bank);
}
// Coloca o Tile ID 1 no mapa do HUD (canto superior esquerdo)
self.gfx.hud.map.set_tile(18, 6, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[0].bank_id = 1;
self.gfx.layers[0].map.set_tile(8, 1, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[0].map.set_tile(8, 2, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[0].map.set_tile(8, 3, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[0].map.set_tile(8, 4, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[0].map.set_tile(8, 5, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[1].bank_id = 1;
self.gfx.layers[1].map.set_tile(9, 1, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[1].map.set_tile(9, 2, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[1].map.set_tile(9, 3, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[1].map.set_tile(9, 4, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[1].map.set_tile(9, 5, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[2].bank_id = 1;
self.gfx.layers[2].map.set_tile(10, 1, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[2].map.set_tile(10, 2, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[2].map.set_tile(10, 3, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[2].map.set_tile(10, 4, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[2].map.set_tile(10, 5, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[3].bank_id = 1;
self.gfx.layers[3].map.set_tile(11, 1, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[3].map.set_tile(11, 2, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[3].map.set_tile(11, 3, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[3].map.set_tile(11, 4, crate::model::Tile { id: 1, ..Default::default() });
self.gfx.layers[3].map.set_tile(11, 5, crate::model::Tile { id: 1, ..Default::default() });
for i in 0..512 {
let s = &mut self.gfx.sprites[i];
s.active = true;
// Distribui os sprites entre os 3 bancos de teste que você criou
s.bank_id = (i % 2) as u8 + 1;
s.tile.id = 1;
// Cria um movimento caótico baseado no index do sprite e no frame_index
let speed = (1 + (i % 3)) as u64;
s.x = (((self.frame_index * speed) + (i as u64 * 10)) % 400) as i32 - 40;
s.y = (((self.frame_index * speed) + (i as u64 * 4)) % 180) as i32;
if i % 2 != 0 {
s.y = 179 - s.y;
}
// Distribui as prioridades de 0 a 4
// Isso vai fazer alguns sprites passarem atrás do cenário e outros na frente
s.priority = (i % 5) as u8;
// Efeito de flip alternado para testar a lógica de espelhamento
s.flip_x = i % 2 == 0;
s.flip_y = i % 3 == 0;
}
}
/// Final do frame: troca buffers.
pub fn end_frame(&mut self) {
self.gfx.present();
}
}