2026-03-24 13:37:20 +00:00

194 lines
6.7 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,
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();
}
}