sevenx_engine 0.2.11

Engine de jogos 2D/3D completa com suporte Android, física, áudio, partículas, tilemap, UI, eventos e sistema 3D avançado com PBR.
Documentation
use crate::sprite::SpriteSheet;

/// Representa um tile individual no mapa.
#[derive(Debug, Clone, Copy)]
pub struct Tile {
    pub tile_id: u32,
    pub solid: bool,
    pub z_index: i32,
}

impl Tile {
    pub fn new(tile_id: u32) -> Self {
        Self {
            tile_id,
            solid: false,
            z_index: 0,
        }
    }

    pub fn with_solid(mut self, solid: bool) -> Self {
        self.solid = solid;
        self
    }

    pub fn with_z_index(mut self, z_index: i32) -> Self {
        self.z_index = z_index;
        self
    }
}

/// Sistema de tilemap para níveis baseados em grid.
pub struct Tilemap {
    pub width: usize,
    pub height: usize,
    pub tile_width: u32,
    pub tile_height: u32,
    pub tiles: Vec<Vec<Option<Tile>>>,
    pub tileset: Option<SpriteSheet>,
}

impl Tilemap {
    pub fn new(width: usize, height: usize, tile_width: u32, tile_height: u32) -> Self {
        let tiles = vec![vec![None; width]; height];
        Self {
            width,
            height,
            tile_width,
            tile_height,
            tiles,
            tileset: None,
        }
    }

    /// Carrega um tileset.
    pub fn load_tileset(&mut self, path: &str) -> Result<(), image::ImageError> {
        let tileset = SpriteSheet::load(path, self.tile_width, self.tile_height)?;
        self.tileset = Some(tileset);
        Ok(())
    }

    /// Define um tile em uma posição específica.
    pub fn set_tile(&mut self, x: usize, y: usize, tile: Option<Tile>) {
        if x < self.width && y < self.height {
            self.tiles[y][x] = tile;
        }
    }

    /// Obtém um tile em uma posição específica.
    pub fn get_tile(&self, x: usize, y: usize) -> Option<&Tile> {
        if x < self.width && y < self.height {
            self.tiles[y][x].as_ref()
        } else {
            None
        }
    }

    /// Converte coordenadas do mundo para coordenadas do tile.
    pub fn world_to_tile(&self, world_x: f32, world_y: f32) -> (usize, usize) {
        let tile_x = (world_x / self.tile_width as f32) as usize;
        let tile_y = (world_y / self.tile_height as f32) as usize;
        (tile_x, tile_y)
    }

    /// Converte coordenadas do tile para coordenadas do mundo.
    pub fn tile_to_world(&self, tile_x: usize, tile_y: usize) -> (f32, f32) {
        let world_x = tile_x as f32 * self.tile_width as f32;
        let world_y = tile_y as f32 * self.tile_height as f32;
        (world_x, world_y)
    }

    /// Verifica se um tile é sólido.
    pub fn is_solid(&self, x: usize, y: usize) -> bool {
        self.get_tile(x, y).map_or(false, |tile| tile.solid)
    }

    /// Preenche uma área retangular com um tile.
    pub fn fill_rect(&mut self, start_x: usize, start_y: usize, width: usize, height: usize, tile: Tile) {
        for y in start_y..(start_y + height).min(self.height) {
            for x in start_x..(start_x + width).min(self.width) {
                self.tiles[y][x] = Some(tile);
            }
        }
    }

    /// Limpa o tilemap.
    pub fn clear(&mut self) {
        for row in &mut self.tiles {
            for tile in row {
                *tile = None;
            }
        }
    }

    /// Renderiza o tilemap no buffer de pixels.
    pub fn render(&self, pixels: &mut [u8], camera_x: f32, camera_y: f32, viewport_width: u32, viewport_height: u32) {
        if let Some(tileset) = &self.tileset {
            let start_tile_x = (camera_x / self.tile_width as f32).floor() as usize;
            let start_tile_y = (camera_y / self.tile_height as f32).floor() as usize;
            let end_tile_x = ((camera_x + viewport_width as f32) / self.tile_width as f32).ceil() as usize;
            let end_tile_y = ((camera_y + viewport_height as f32) / self.tile_height as f32).ceil() as usize;

            for tile_y in start_tile_y..end_tile_y.min(self.height) {
                for tile_x in start_tile_x..end_tile_x.min(self.width) {
                    if let Some(tile) = &self.tiles[tile_y][tile_x] {
                        let world_x = tile_x as f32 * self.tile_width as f32;
                        let world_y = tile_y as f32 * self.tile_height as f32;
                        let screen_x = (world_x - camera_x) as i32;
                        let screen_y = (world_y - camera_y) as i32;

                        self.render_tile(pixels, tileset, tile.tile_id, screen_x, screen_y, viewport_width);
                    }
                }
            }
        }
    }

    fn render_tile(&self, pixels: &mut [u8], tileset: &SpriteSheet, tile_id: u32, screen_x: i32, screen_y: i32, viewport_width: u32) {
        let tiles_per_row = tileset.width / self.tile_width;
        let tile_row = tile_id / tiles_per_row;
        let tile_col = tile_id % tiles_per_row;
        let tile_x_offset = tile_col * self.tile_width;
        let tile_y_offset = tile_row * self.tile_height;

        for y in 0..self.tile_height {
            for x in 0..self.tile_width {
                let px = screen_x + x as i32;
                let py = screen_y + y as i32;

                if px < 0 || py < 0 || px >= viewport_width as i32 || py >= viewport_width as i32 {
                    continue;
                }

                let tileset_x = tile_x_offset + x;
                let tileset_y = tile_y_offset + y;
                let tileset_index = ((tileset_y * tileset.width) + tileset_x) as usize * 4;

                if tileset_index + 3 < tileset.rgba_data.len() {
                    let color = &tileset.rgba_data[tileset_index..tileset_index + 4];
                    if color[3] > 0 {
                        let screen_index = ((py as u32 * viewport_width) + px as u32) as usize * 4;
                        if screen_index + 3 < pixels.len() {
                            pixels[screen_index..screen_index + 4].copy_from_slice(color);
                        }
                    }
                }
            }
        }
    }
}