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, pub last_frame_cpu_time_us: 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, last_frame_cpu_time_us: 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) { let start = std::time::Instant::now(); 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 self.last_frame_cpu_time_us = start.elapsed().as_micros() as u64; // Calcula quanto tempo a "CPU simulada" trabalhou de verdade } /// 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 Preto self.gfx.clear(Color::GRAY); // SETUP BANCO 0 (Tiles 8x8) if self.gfx.banks[0].is_none() { let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size8, 128, 128); // Define Cor na Paleta 0, Índice 1 = VERDE bank.palettes[0][1] = Color::GREEN; let tile_size = 8; let start_x = 8; // Tile ID 1 for y in 0..tile_size { for x in 0..tile_size { let idx = y * 128 + (start_x + x); bank.pixel_indices[idx] = 1; // Atribui o índice da paleta } } self.gfx.banks[0] = Some(bank); } // SETUP BANCO 1 (Tiles 16x16) if self.gfx.banks[1].is_none() { let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128); // Define Cor na Paleta 0, Índice 1 = VERMELHO bank.palettes[0][1] = Color::RED; let tile_size = 16; let start_x = 16; // Tile ID 1 for y in 0..tile_size { for x in 0..tile_size { let idx = y * 128 + (start_x + x); bank.pixel_indices[idx] = 1; } } self.gfx.banks[1] = Some(bank); } // SETUP BANCO 2 (Tiles 16x16 para Sprites) if self.gfx.banks[2].is_none() { let mut bank = crate::model::TileBank::new(crate::model::TileSize::Size16, 128, 128); bank.palettes[0][1] = Color::INDIGO; bank.palettes[1][1] = Color::YELLOW; bank.palettes[2][1] = Color::RED; bank.palettes[3][1] = Color::GREEN; bank.palettes[4][1] = Color::ORANGE; let tile_size = 16; let start_x = 16; for y in 0..tile_size { for x in 0..tile_size { let idx = y * 128 + (start_x + x); bank.pixel_indices[idx] = 1; } } self.gfx.banks[2] = Some(bank); } // --- LÓGICA DE ESTADO --- // HUD self.gfx.hud.map.set_tile(18, 6, crate::model::Tile { id: 1, palette_id: 0, ..Default::default() }); // Camadas de Jogo (usando Banco 1 - Vermelho) for i in 0..4 { self.gfx.layers[i].bank_id = 1; self.gfx.layers[i].map.set_tile(8 + i, 1 + i, crate::model::Tile { id: 1, palette_id: 0, ..Default::default() }); } // 512 Sprites (usando Banco 2 - Indigo e Amarelo) for i in 0..510 { let s = &mut self.gfx.sprites[i]; s.active = true; s.bank_id = 2; s.tile.id = 1; // Palette Swap: pares Indigo (0), ímpares Amarelo (1) s.tile.palette_id = (i % 2) as u8; let speed = (1 + (i % 3)) as u64; s.x = (((self.frame_index * speed) + (i as u64 * 10)) % 400) as i32 - 40; let y_base = (((self.frame_index * speed) + (i as u64 * 4)) % 180) as i32; s.y = if i % 2 == 0 { y_base } else { 179 - y_base }; s.priority = (i % 5) as u8; s.flip_x = i % 2 == 0; s.flip_y = i % 3 == 0; } // --- INTERATIVIDADE COM TOUCH --- let cursor = &mut self.gfx.sprites[511]; cursor.active = true; cursor.bank_id = 2; // Banco Verde cursor.tile.id = 1; cursor.priority = 4; // Se o dedo/mouse estiver pressionado agora (down) if self.touch.f.down { cursor.x = self.touch.x - 4; // Centraliza tile 8x8 cursor.y = self.touch.y - 4; cursor.tile.palette_id = 2; // RED } else { cursor.tile.palette_id = 3; // GREEN } // --- INTERATIVIDADE COM INPUT (Gamepad) --- let player = &mut self.gfx.sprites[510]; // Usaremos outro sprite para o Pad player.active = true; player.bank_id = 2; player.tile.id = 1; player.priority = 4; player.tile.palette_id = 4; // ORANGE // Movimento contínuo (enquanto segura) let move_speed = 2; if self.pad.up.down { player.y -= move_speed; } if self.pad.down.down { player.y += move_speed; } if self.pad.left.down { player.x -= move_speed; } if self.pad.right.down { player.x += move_speed; } // Trigger (apenas no frame que apertou) if self.pad.a.down { player.tile.palette_id = 2; } // Exemplo de uso do released if self.pad.start.released { // Se soltar o Start, podemos pausar algo futuramente } // Post-FX Fade Pulsante let pulse = (self.frame_index / 4) % 64; let level = if pulse > 31 { 63 - pulse } else { pulse }; self.gfx.scene_fade_level = level as u8; self.gfx.scene_fade_color = Color::BLACK; self.gfx.hud_fade_level = 31; } /// Final do frame: troca buffers. pub fn end_frame(&mut self) { self.gfx.present(); } }