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(); } }