//! Map rendering functionality. use crate::constants::{BOARD_CELL_OFFSET, CELL_SIZE}; use crate::map::layout::TILE_MAP; use crate::texture::sprite::{AtlasTile, SpriteAtlas}; use crate::texture::text::TextTexture; use glam::Vec2; use sdl2::pixels::Color; use sdl2::rect::{Point, Rect}; use sdl2::render::{Canvas, RenderTarget}; use crate::error::{EntityError, GameError, GameResult}; /// Handles rendering operations for the map. pub struct MapRenderer; impl MapRenderer { /// Renders the map to the given canvas. /// /// This function draws the static map texture to the screen at the correct /// position and scale. pub fn render_map(canvas: &mut Canvas, atlas: &mut SpriteAtlas, map_tiles: &[AtlasTile]) { for (y, row) in TILE_MAP.iter().enumerate() { for (x, &tile_index) in row.iter().enumerate() { let mut tile = map_tiles[tile_index]; tile.color = Some(Color::RGB(0x20, 0x20, 0xf9)); let dest = Rect::new( (BOARD_CELL_OFFSET.x as usize * CELL_SIZE as usize + x * CELL_SIZE as usize) as i32, (BOARD_CELL_OFFSET.y as usize * CELL_SIZE as usize + y * CELL_SIZE as usize) as i32, CELL_SIZE, CELL_SIZE, ); if let Err(e) = tile.render(canvas, atlas, dest) { tracing::error!("Failed to render map tile: {}", e); } } } } /// Renders a debug visualization with cursor-based highlighting. /// /// This function provides interactive debugging by highlighting the nearest node /// to the cursor, showing its ID, and highlighting its connections. pub fn debug_render_with_cursor( graph: &crate::entity::graph::Graph, canvas: &mut Canvas, text_renderer: &mut TextTexture, atlas: &mut SpriteAtlas, cursor_pos: Vec2, ) -> GameResult<()> { // Find the nearest node to the cursor let nearest_node = Self::find_nearest_node(graph, cursor_pos); // Draw all connections in blue canvas.set_draw_color(Color::RGB(0, 0, 128)); // Dark blue for regular connections for i in 0..graph.node_count() { let node = graph.get_node(i).ok_or(GameError::Entity(EntityError::NodeNotFound(i)))?; let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); for edge in graph.adjacency_list[i].edges() { let end_pos = graph .get_node(edge.target) .ok_or(GameError::Entity(EntityError::NodeNotFound(edge.target)))? .position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); canvas .draw_line((pos.x as i32, pos.y as i32), (end_pos.x as i32, end_pos.y as i32)) .map_err(|e| GameError::Sdl(e.to_string()))?; } } // Draw all nodes in green canvas.set_draw_color(Color::RGB(0, 128, 0)); // Dark green for regular nodes for i in 0..graph.node_count() { let node = graph.get_node(i).ok_or(GameError::Entity(EntityError::NodeNotFound(i)))?; let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); canvas .fill_rect(Rect::new(0, 0, 3, 3).centered_on(Point::new(pos.x as i32, pos.y as i32))) .map_err(|e| GameError::Sdl(e.to_string()))?; } // Highlight connections from the nearest node in bright blue if let Some(nearest_id) = nearest_node { let nearest_pos = graph .get_node(nearest_id) .ok_or(GameError::Entity(EntityError::NodeNotFound(nearest_id)))? .position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); canvas.set_draw_color(Color::RGB(0, 255, 255)); // Bright cyan for highlighted connections for edge in graph.adjacency_list[nearest_id].edges() { let end_pos = graph .get_node(edge.target) .ok_or(GameError::Entity(EntityError::NodeNotFound(edge.target)))? .position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); canvas .draw_line( (nearest_pos.x as i32, nearest_pos.y as i32), (end_pos.x as i32, end_pos.y as i32), ) .map_err(|e| GameError::Sdl(e.to_string()))?; } // Highlight the nearest node in bright green canvas.set_draw_color(Color::RGB(0, 255, 0)); // Bright green for highlighted node canvas .fill_rect(Rect::new(0, 0, 5, 5).centered_on(Point::new(nearest_pos.x as i32, nearest_pos.y as i32))) .map_err(|e| GameError::Sdl(e.to_string()))?; // Draw node ID text (small, offset to top right) text_renderer.set_scale(0.5); // Small text let id_text = format!("#{nearest_id}"); let text_pos = glam::UVec2::new( (nearest_pos.x + 4.0) as u32, // Offset to the right (nearest_pos.y - 6.0) as u32, // Offset to the top ); if let Err(e) = text_renderer.render(canvas, atlas, &id_text, text_pos) { tracing::error!("Failed to render node ID text: {}", e); } } Ok(()) } /// Finds the nearest node to the given cursor position. pub fn find_nearest_node(graph: &crate::entity::graph::Graph, cursor_pos: Vec2) -> Option { let mut nearest_id = None; let mut nearest_distance = f32::INFINITY; for i in 0..graph.node_count() { if let Some(node) = graph.get_node(i) { let node_pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2(); let distance = cursor_pos.distance(node_pos); if distance < nearest_distance { nearest_distance = distance; nearest_id = Some(i); } } } nearest_id } }