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