diff --git a/src/main.rs b/src/main.rs index a9c47bc..9807a5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -309,6 +309,7 @@ impl eframe::App for DungeonApp { ); } self.draw_add_overlay(&painter, &geometry); + self.draw_add_tool_ghost(&painter, &geometry); self.draw_resize_overlay(&painter, &geometry); self.draw_corridor_hover_overlay(&painter, &geometry); self.draw_door_hover_overlay(&painter, &geometry); @@ -993,6 +994,209 @@ impl DungeonApp { draw_dashed_line(painter, start, end, stroke, 8.0, 6.0); } + // Draw ghost preview for the currently active add tool. + fn draw_add_tool_ghost(&self, painter: &egui::Painter, geometry: &GridGeometry) { + let tool = self.settings.add_tool; + if tool == AddTool::None || tool == AddTool::Corridor { + return; + } + + let Some(pointer_pos) = painter.ctx().pointer_hover_pos() else { + return; + }; + let Some((grid_x, grid_y)) = pointer_to_grid(pointer_pos, geometry) else { + return; + }; + let cell = (grid_x.floor() as usize, grid_y.floor() as usize); + + let ghost_alpha = 0.35; + + match tool { + AddTool::Room => { + let width = self.settings.add_room_width.max(1); + let height = self.settings.add_room_height.max(1); + let max_x = self.settings.cols.saturating_sub(cell.0); + let max_y = self.settings.rows.saturating_sub(cell.1); + let w = width.min(max_x).max(1); + let h = height.min(max_y).max(1); + + let left = geometry.rect.left() + cell.0 as f32 * geometry.cell_size; + let top = geometry.rect.top() + cell.1 as f32 * geometry.cell_size; + let right = left + w as f32 * geometry.cell_size; + let bottom = top + h as f32 * geometry.cell_size; + let rect = + egui::Rect::from_min_max(egui::pos2(left, top), egui::pos2(right, bottom)); + + let fill = Color32::from_rgb(70, 120, 160).gamma_multiply(ghost_alpha); + painter.rect_filled(rect, 4.0, fill); + painter.rect_stroke( + rect, + 4.0, + Stroke::new( + 1.5, + Color32::from_rgb(150, 200, 240).gamma_multiply(ghost_alpha + 0.3), + ), + egui::StrokeKind::Middle, + ); + } + AddTool::Text => { + let text = self.settings.add_text_value.trim(); + if text.is_empty() { + return; + } + let font_size = self.settings.add_text_font_size.max(8); + let rect = text_label_rect( + geometry, + &layout::TextLabel { + cell, + text: text.to_string(), + font_size, + }, + ); + + let fill = Color32::from_rgb(50, 50, 70).gamma_multiply(ghost_alpha); + painter.rect_filled(rect.expand(4.0), 4.0, fill); + painter.rect_stroke( + rect.expand(4.0), + 4.0, + Stroke::new( + 1.0, + Color32::from_rgb(180, 180, 220).gamma_multiply(ghost_alpha + 0.2), + ), + egui::StrokeKind::Middle, + ); + + let color = Color32::WHITE.gamma_multiply(ghost_alpha + 0.1); + painter.text( + rect.center(), + egui::Align2::CENTER_CENTER, + text, + egui::FontId::proportional(font_size as f32), + color, + ); + } + AddTool::StartMarker + | AddTool::EndMarker + | AddTool::TrapMarker + | AddTool::MonsterMarker => { + let size = self.settings.add_marker_size.max(1); + let origin = + centered_marker_origin(cell, size, self.settings.cols, self.settings.rows); + + let rect = marker_rect(&layout::AreaMarker { cell: origin, size }, geometry); + + let base_color = match tool { + AddTool::StartMarker => Color32::from_rgb(60, 220, 200), + AddTool::EndMarker => Color32::from_rgb(240, 90, 90), + AddTool::TrapMarker => Color32::from_rgb(150, 80, 230), + AddTool::MonsterMarker => Color32::from_rgb(200, 50, 50), + _ => Color32::WHITE, + }; + + let fill = base_color.gamma_multiply(ghost_alpha); + painter.rect_filled(rect, 4.0, fill); + painter.rect_stroke( + rect, + 4.0, + Stroke::new( + 1.5, + Color32::from_rgb(255, 255, 255).gamma_multiply(ghost_alpha + 0.2), + ), + egui::StrokeKind::Middle, + ); + + let label = match tool { + AddTool::StartMarker => "S", + AddTool::EndMarker => "E", + AddTool::TrapMarker => "T", + AddTool::MonsterMarker => "M", + _ => "?", + }; + let text_color = Color32::WHITE.gamma_multiply(ghost_alpha + 0.15); + let font_size = (geometry.cell_size * size as f32 * 0.5).clamp(14.0, 72.0); + painter.text( + rect.center(), + egui::Align2::CENTER_CENTER, + label, + egui::FontId::proportional(font_size), + text_color, + ); + } + AddTool::Staircase => { + let width = self.settings.add_stair_width.max(1); + let height = self.settings.add_stair_height.max(1); + let max_x = self.settings.cols.saturating_sub(width); + let max_y = self.settings.rows.saturating_sub(height); + let w = width.min(max_x).max(1); + let h = height.min(max_y).max(1); + + let left = geometry.rect.left() + cell.0 as f32 * geometry.cell_size; + let top = geometry.rect.top() + cell.1 as f32 * geometry.cell_size; + let right = left + w as f32 * geometry.cell_size; + let bottom = top + h as f32 * geometry.cell_size; + let rect = + egui::Rect::from_min_max(egui::pos2(left, top), egui::pos2(right, bottom)); + + let fill = Color32::from_rgb(200, 150, 50).gamma_multiply(ghost_alpha); + painter.rect_filled(rect, 4.0, fill); + painter.rect_stroke( + rect, + 4.0, + Stroke::new( + 1.5, + Color32::from_rgb(220, 180, 80).gamma_multiply(ghost_alpha + 0.2), + ), + egui::StrokeKind::Middle, + ); + + let steps = (h as f32 * 2.0).round() as usize; + let step_height = (rect.height() / (steps as f32)).max(1.0); + let line_color = Color32::from_rgb(150, 100, 30).gamma_multiply(ghost_alpha + 0.15); + for i in 0..steps { + let y = rect.top() + (i as f32 * step_height) + step_height * 0.5; + painter.line_segment( + [ + egui::pos2(rect.left() + 2.0, y), + egui::pos2(rect.right() - 2.0, y), + ], + Stroke::new(1.5, line_color), + ); + } + } + AddTool::Archway | AddTool::Door | AddTool::LockedDoor | AddTool::SecretDoor => { + // Show ghost preview on the hovered door edge. + if let Some((edge_a, edge_b)) = self.door_edge_at_pointer(pointer_pos, geometry) { + let door_width_cells = 1; + let style = match tool { + AddTool::Archway => DoorLineStyle::Dotted, + AddTool::Door => DoorLineStyle::LongDash, + AddTool::LockedDoor => DoorLineStyle::ShortDash, + AddTool::SecretDoor => DoorLineStyle::DashDotDot, + _ => DoorLineStyle::Solid, + }; + let color = match tool { + AddTool::Archway => Color32::from_rgb(230, 140, 60), + AddTool::Door => Color32::from_rgb(80, 200, 120), + AddTool::LockedDoor => Color32::from_rgb(220, 70, 70), + AddTool::SecretDoor => Color32::from_rgb(170, 80, 170), + _ => Color32::WHITE, + }; + let ghost_color = color.gamma_multiply(ghost_alpha); + draw_door_line( + painter, + geometry, + edge_a, + edge_b, + door_width_cells, + Stroke::new((geometry.cell_size / 5.0).max(1.0), ghost_color), + style, + ); + } + } + AddTool::None | AddTool::Corridor => {} + } + } + // Add a room anchored to the given grid cell if space allows. fn add_room_at_cell(&mut self, cell: (usize, usize)) { self.push_undo_snapshot();