zoom factor
This commit is contained in:
@@ -320,6 +320,8 @@ pub struct Board {
|
|||||||
pub portals: Vec<PortalDef>,
|
pub portals: Vec<PortalDef>,
|
||||||
/// Optional font override for this board. When `None`, the app default is used.
|
/// Optional font override for this board. When `None`, the app default is used.
|
||||||
pub font: Option<FontSpec>,
|
pub font: Option<FontSpec>,
|
||||||
|
/// Integer scale factor applied to tiles when rendering this board. 1 = natural size.
|
||||||
|
pub zoom: u32,
|
||||||
/// Named Rhai scripts available on this board: script name → source text.
|
/// Named Rhai scripts available on this board: script name → source text.
|
||||||
///
|
///
|
||||||
/// All script source lives here; [`ObjectDef`]s and [`Board::board_script_name`]
|
/// All script source lives here; [`ObjectDef`]s and [`Board::board_script_name`]
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ pub struct MapHeader {
|
|||||||
/// Name of the board-level script in the `[scripts]` table, if any.
|
/// Name of the board-level script in the `[scripts]` table, if any.
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub board_script_name: Option<String>,
|
pub board_script_name: Option<String>,
|
||||||
|
/// Integer tile scale factor. 1 = natural size. Omitted from files when 1 (the default).
|
||||||
|
#[serde(default = "default_zoom", skip_serializing_if = "is_zoom_default")]
|
||||||
|
pub zoom: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `[font]` section of a map file, specifying a bitmap font for this board.
|
/// The `[font]` section of a map file, specifying a bitmap font for this board.
|
||||||
@@ -150,6 +153,8 @@ pub(crate) struct MapFileObjectEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_true() -> bool { true }
|
fn default_true() -> bool { true }
|
||||||
|
fn default_zoom() -> u32 { 1 }
|
||||||
|
fn is_zoom_default(z: &u32) -> bool { *z == 1 }
|
||||||
|
|
||||||
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
|
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
|
||||||
/// Returns opaque black on any parse failure.
|
/// Returns opaque black on any parse failure.
|
||||||
@@ -282,6 +287,7 @@ impl TryFrom<MapFile> for Board {
|
|||||||
objects,
|
objects,
|
||||||
portals: mf.portals,
|
portals: mf.portals,
|
||||||
font,
|
font,
|
||||||
|
zoom: mf.map.zoom,
|
||||||
scripts: mf.scripts,
|
scripts: mf.scripts,
|
||||||
board_script_name: mf.map.board_script_name,
|
board_script_name: mf.map.board_script_name,
|
||||||
})
|
})
|
||||||
@@ -399,6 +405,7 @@ impl From<&Board> for MapFile {
|
|||||||
height: board.height,
|
height: board.height,
|
||||||
player_start: [board.player.x, board.player.y],
|
player_start: [board.player.x, board.player.y],
|
||||||
board_script_name: board.board_script_name.clone(),
|
board_script_name: board.board_script_name.clone(),
|
||||||
|
zoom: board.zoom,
|
||||||
},
|
},
|
||||||
palette,
|
palette,
|
||||||
grid: GridData { content },
|
grid: GridData { content },
|
||||||
|
|||||||
@@ -227,6 +227,10 @@ pub(crate) fn show_editor_panel(
|
|||||||
FontDialogState::from_spec(board.font.as_ref());
|
FontDialogState::from_spec(board.font.as_ref());
|
||||||
editor.font_dialog_open = true;
|
editor.font_dialog_open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
ui.label("Zoom");
|
||||||
|
ui.add(egui::Slider::new(&mut board.zoom, 1..=8).text("×"));
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorTab::Scripts => {
|
EditorTab::Scripts => {
|
||||||
|
|||||||
@@ -197,8 +197,9 @@ impl eframe::App for App {
|
|||||||
// Snapshot the active font for this frame so all rendering uses the same one.
|
// Snapshot the active font for this frame so all rendering uses the same one.
|
||||||
// We re-borrow active_font here since apply_font_spec may have changed board_font.
|
// We re-borrow active_font here since apply_font_spec may have changed board_font.
|
||||||
let font = self.board_font.as_ref().unwrap_or(&self.default_font);
|
let font = self.board_font.as_ref().unwrap_or(&self.default_font);
|
||||||
let tw = font.tile_w as f32;
|
let zoom = self.state.board.zoom;
|
||||||
let th = font.tile_h as f32;
|
let tw = font.tile_w as f32 * zoom as f32;
|
||||||
|
let th = font.tile_h as f32 * zoom as f32;
|
||||||
|
|
||||||
// --- Board rendering ---
|
// --- Board rendering ---
|
||||||
// The board is drawn using the egui Painter API (direct 2D drawing).
|
// The board is drawn using the egui Painter API (direct 2D drawing).
|
||||||
@@ -211,7 +212,7 @@ impl eframe::App for App {
|
|||||||
// Edit mode: ScrollArea lets the user scroll when the board overflows.
|
// Edit mode: ScrollArea lets the user scroll when the board overflows.
|
||||||
egui::ScrollArea::new([true, true]).show(ui, |ui| {
|
egui::ScrollArea::new([true, true]).show(ui, |ui| {
|
||||||
let (rect, response) = ui.allocate_exact_size(board_px, Sense::click());
|
let (rect, response) = ui.allocate_exact_size(board_px, Sense::click());
|
||||||
draw_board(ui.painter(), rect.min, &self.state.board, font);
|
draw_board(ui.painter(), rect.min, &self.state.board, font, zoom);
|
||||||
|
|
||||||
// When the Objects tab is active, overlay the object highlights.
|
// When the Objects tab is active, overlay the object highlights.
|
||||||
if self.editor.tab == EditorTab::Objects {
|
if self.editor.tab == EditorTab::Objects {
|
||||||
@@ -221,6 +222,7 @@ impl eframe::App for App {
|
|||||||
&self.state.board,
|
&self.state.board,
|
||||||
font,
|
font,
|
||||||
self.editor.selected_object,
|
self.editor.selected_object,
|
||||||
|
zoom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,8 +266,8 @@ impl eframe::App for App {
|
|||||||
// Play mode: player-centered viewport, no scroll bars.
|
// Play mode: player-centered viewport, no scroll bars.
|
||||||
let available = ui.available_rect_before_wrap();
|
let available = ui.available_rect_before_wrap();
|
||||||
let player = self.state.board.player;
|
let player = self.state.board.player;
|
||||||
let origin = board_origin(available, board_w, board_h, player, font);
|
let origin = board_origin(available, board_w, board_h, player, font, zoom);
|
||||||
draw_board(ui.painter(), origin, &self.state.board, font);
|
draw_board(ui.painter(), origin, &self.state.board, font, zoom);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+24
-12
@@ -65,7 +65,7 @@ pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph, fo
|
|||||||
///
|
///
|
||||||
/// `origin` is the pixel position of cell `(0, 0)` — the top-left corner of
|
/// `origin` is the pixel position of cell `(0, 0)` — the top-left corner of
|
||||||
/// the board within the panel. Cell positions are computed from `origin` using
|
/// the board within the panel. Cell positions are computed from `origin` using
|
||||||
/// the font's tile dimensions.
|
/// the font's tile dimensions multiplied by `zoom`.
|
||||||
pub(crate) fn draw_glyph(
|
pub(crate) fn draw_glyph(
|
||||||
painter: &egui::Painter,
|
painter: &egui::Painter,
|
||||||
origin: Pos2,
|
origin: Pos2,
|
||||||
@@ -73,9 +73,10 @@ pub(crate) fn draw_glyph(
|
|||||||
y: usize,
|
y: usize,
|
||||||
glyph: &Glyph,
|
glyph: &Glyph,
|
||||||
font: &BitmapFont,
|
font: &BitmapFont,
|
||||||
|
zoom: u32,
|
||||||
) {
|
) {
|
||||||
let tw = font.tile_w as f32;
|
let tw = font.tile_w as f32 * zoom as f32;
|
||||||
let th = font.tile_h as f32;
|
let th = font.tile_h as f32 * zoom as f32;
|
||||||
let tl = origin + vec2(x as f32 * tw, y as f32 * th);
|
let tl = origin + vec2(x as f32 * tw, y as f32 * th);
|
||||||
let rect = Rect::from_min_size(tl, vec2(tw, th));
|
let rect = Rect::from_min_size(tl, vec2(tw, th));
|
||||||
paint_glyph(painter, rect, glyph, font);
|
paint_glyph(painter, rect, glyph, font);
|
||||||
@@ -85,17 +86,24 @@ pub(crate) fn draw_glyph(
|
|||||||
///
|
///
|
||||||
/// Rendering order: grid (floor) → objects → player. Objects are drawn on top
|
/// Rendering order: grid (floor) → objects → player. Objects are drawn on top
|
||||||
/// of their grid cell's background, which is revealed if the object moves away.
|
/// of their grid cell's background, which is revealed if the object moves away.
|
||||||
pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, font: &BitmapFont) {
|
/// `zoom` scales each tile by an integer factor.
|
||||||
|
pub(crate) fn draw_board(
|
||||||
|
painter: &egui::Painter,
|
||||||
|
origin: Pos2,
|
||||||
|
board: &Board,
|
||||||
|
font: &BitmapFont,
|
||||||
|
zoom: u32,
|
||||||
|
) {
|
||||||
// Pass 1: grid floor.
|
// Pass 1: grid floor.
|
||||||
for y in 0..board.height {
|
for y in 0..board.height {
|
||||||
for x in 0..board.width {
|
for x in 0..board.width {
|
||||||
let (glyph, _) = board.get(x, y);
|
let (glyph, _) = board.get(x, y);
|
||||||
draw_glyph(painter, origin, x, y, glyph, font);
|
draw_glyph(painter, origin, x, y, glyph, font, zoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pass 2: objects drawn on top of the floor.
|
// Pass 2: objects drawn on top of the floor.
|
||||||
for obj in &board.objects {
|
for obj in &board.objects {
|
||||||
draw_glyph(painter, origin, obj.x, obj.y, &obj.glyph, font);
|
draw_glyph(painter, origin, obj.x, obj.y, &obj.glyph, font, zoom);
|
||||||
}
|
}
|
||||||
// Pass 3: player overlay. Will be removed once the player becomes a scripted object.
|
// Pass 3: player overlay. Will be removed once the player becomes a scripted object.
|
||||||
let player_glyph = Glyph::player();
|
let player_glyph = Glyph::player();
|
||||||
@@ -106,6 +114,7 @@ pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, f
|
|||||||
board.player.y as usize,
|
board.player.y as usize,
|
||||||
&player_glyph,
|
&player_glyph,
|
||||||
font,
|
font,
|
||||||
|
zoom,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +123,18 @@ pub(crate) fn draw_board(painter: &egui::Painter, origin: Pos2, board: &Board, f
|
|||||||
/// Dims the entire board with a semi-transparent fill, then for each
|
/// Dims the entire board with a semi-transparent fill, then for each
|
||||||
/// [`ObjectDef`](crate::game::ObjectDef) redraws that cell at full brightness
|
/// [`ObjectDef`](crate::game::ObjectDef) redraws that cell at full brightness
|
||||||
/// and outlines it in white. The selected object (by index into
|
/// and outlines it in white. The selected object (by index into
|
||||||
/// `board.objects`) gets a yellow outline instead.
|
/// `board.objects`) gets a yellow outline instead. `zoom` must match the zoom
|
||||||
|
/// used to draw the board.
|
||||||
pub(crate) fn draw_object_overlays(
|
pub(crate) fn draw_object_overlays(
|
||||||
painter: &egui::Painter,
|
painter: &egui::Painter,
|
||||||
origin: Pos2,
|
origin: Pos2,
|
||||||
board: &Board,
|
board: &Board,
|
||||||
font: &BitmapFont,
|
font: &BitmapFont,
|
||||||
selected: Option<usize>,
|
selected: Option<usize>,
|
||||||
|
zoom: u32,
|
||||||
) {
|
) {
|
||||||
let tw = font.tile_w as f32;
|
let tw = font.tile_w as f32 * zoom as f32;
|
||||||
let th = font.tile_h as f32;
|
let th = font.tile_h as f32 * zoom as f32;
|
||||||
|
|
||||||
// Dim the whole board so non-object cells recede visually.
|
// Dim the whole board so non-object cells recede visually.
|
||||||
let board_rect = Rect::from_min_size(
|
let board_rect = Rect::from_min_size(
|
||||||
@@ -166,16 +177,17 @@ pub(crate) fn pos_to_cell(origin: Pos2, pos: Pos2, tile_w: f32, tile_h: f32) ->
|
|||||||
///
|
///
|
||||||
/// If the board fits along an axis, it is centered. If it overflows, the
|
/// If the board fits along an axis, it is centered. If it overflows, the
|
||||||
/// viewport is centered on the player and clamped so no empty space appears
|
/// viewport is centered on the player and clamped so no empty space appears
|
||||||
/// at the board edges.
|
/// at the board edges. `zoom` scales tile dimensions before computing pixel sizes.
|
||||||
pub(crate) fn board_origin(
|
pub(crate) fn board_origin(
|
||||||
available: Rect,
|
available: Rect,
|
||||||
board_w: usize,
|
board_w: usize,
|
||||||
board_h: usize,
|
board_h: usize,
|
||||||
player: Player,
|
player: Player,
|
||||||
font: &BitmapFont,
|
font: &BitmapFont,
|
||||||
|
zoom: u32,
|
||||||
) -> Pos2 {
|
) -> Pos2 {
|
||||||
let tw = font.tile_w as f32;
|
let tw = font.tile_w as f32 * zoom as f32;
|
||||||
let th = font.tile_h as f32;
|
let th = font.tile_h as f32 * zoom as f32;
|
||||||
let board_px_w = board_w as f32 * tw;
|
let board_px_w = board_w as f32 * tw;
|
||||||
let board_px_h = board_h as f32 * th;
|
let board_px_h = board_h as f32 * th;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user