workspaceify
This commit is contained in:
Generated
+20
-4
@@ -658,6 +658,12 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "color"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ec7c5eb7a16992b1904d76c517d170ab353b0e0b3d5a0c81a8a0cd1037893cf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -1705,17 +1711,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kiln"
|
name = "kiln-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"eframe",
|
"color",
|
||||||
"image",
|
|
||||||
"rfd",
|
|
||||||
"rhai",
|
"rhai",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kiln-egui"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"color",
|
||||||
|
"eframe",
|
||||||
|
"image",
|
||||||
|
"kiln-core",
|
||||||
|
"rfd",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "leb128fmt"
|
name = "leb128fmt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
+3
-12
@@ -1,12 +1,3 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "kiln"
|
members = ["kiln-core", "kiln-egui"]
|
||||||
version = "0.1.0"
|
resolver = "2"
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
eframe = "0.33.3"
|
|
||||||
rhai = "1"
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
toml = { version = "0.8", features = ["preserve_order"] }
|
|
||||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
|
||||||
rfd = "0.15"
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "kiln-core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
color = "0.3.3"
|
||||||
|
rhai = "1"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
toml = { version = "0.8", features = ["preserve_order"] }
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use eframe::egui::Color32;
|
use color::Rgba8;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// The visual representation of a single board cell.
|
/// The visual representation of a single board cell.
|
||||||
///
|
///
|
||||||
@@ -14,14 +15,23 @@ use std::collections::HashMap;
|
|||||||
/// `Glyph` values come from the map file palette and are set at load time.
|
/// `Glyph` values come from the map file palette and are set at load time.
|
||||||
/// The player is the only entity whose glyph is hardcoded at runtime
|
/// The player is the only entity whose glyph is hardcoded at runtime
|
||||||
/// (see [`Glyph::player`]).
|
/// (see [`Glyph::player`]).
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Glyph {
|
pub struct Glyph {
|
||||||
/// Tile index into the board's bitmap font (left-to-right, top-to-bottom).
|
/// Tile index into the board's bitmap font (left-to-right, top-to-bottom).
|
||||||
pub tile: u32,
|
pub tile: u32,
|
||||||
/// Foreground color, applied to non-background pixels of the tile.
|
/// Foreground color, applied to non-background pixels of the tile.
|
||||||
pub fg: Color32,
|
pub fg: Rgba8,
|
||||||
/// Background color, drawn as a filled rectangle behind the tile.
|
/// Background color, drawn as a filled rectangle behind the tile.
|
||||||
pub bg: Color32,
|
pub bg: Rgba8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Glyph {
|
||||||
|
/// Hash via packed u32 representations so the impl stays in sync with Eq.
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.tile.hash(state);
|
||||||
|
self.fg.to_u32().hash(state);
|
||||||
|
self.bg.to_u32().hash(state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Glyph {
|
impl Glyph {
|
||||||
@@ -33,8 +43,8 @@ impl Glyph {
|
|||||||
pub const fn player() -> Self {
|
pub const fn player() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tile: 64,
|
tile: 64,
|
||||||
fg: Color32::LIGHT_BLUE,
|
fg: Rgba8 { r: 173, g: 216, b: 230, a: 255 }, // light blue
|
||||||
bg: Color32::BLACK,
|
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 }, // black
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,24 +154,24 @@ impl Archetype {
|
|||||||
match self {
|
match self {
|
||||||
Archetype::Empty => Glyph {
|
Archetype::Empty => Glyph {
|
||||||
tile: 32,
|
tile: 32,
|
||||||
fg: Color32::BLACK,
|
fg: Rgba8 { r: 0, g: 0, b: 0, a: 255 },
|
||||||
bg: Color32::BLACK,
|
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 },
|
||||||
},
|
},
|
||||||
Archetype::Wall => Glyph {
|
Archetype::Wall => Glyph {
|
||||||
tile: 35,
|
tile: 35,
|
||||||
fg: Color32::from_rgb(0x80, 0x80, 0x80),
|
fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 },
|
||||||
bg: Color32::from_rgb(0x60, 0x60, 0x60),
|
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 },
|
||||||
},
|
},
|
||||||
Archetype::Object => Glyph {
|
Archetype::Object => Glyph {
|
||||||
tile: 63,
|
tile: 63,
|
||||||
fg: Color32::YELLOW,
|
fg: Rgba8 { r: 255, g: 255, b: 0, a: 255 }, // yellow
|
||||||
bg: Color32::BLACK,
|
bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 },
|
||||||
},
|
},
|
||||||
// Visually distinct so malformed map files are immediately obvious.
|
// Visually distinct so malformed map files are immediately obvious.
|
||||||
Archetype::ErrorBlock => Glyph {
|
Archetype::ErrorBlock => Glyph {
|
||||||
tile: 63,
|
tile: 63,
|
||||||
fg: Color32::YELLOW,
|
fg: Rgba8 { r: 255, g: 255, b: 0, a: 255 }, // yellow on red
|
||||||
bg: Color32::RED,
|
bg: Rgba8 { r: 255, g: 0, b: 0, a: 255 },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,6 +333,13 @@ impl Board {
|
|||||||
&mut self.cells[y * self.width + x]
|
&mut self.cells[y * self.width + x]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a slice of all cells in row-major order.
|
||||||
|
///
|
||||||
|
/// Useful for iterating over the full board (e.g. to collect unique glyphs).
|
||||||
|
pub fn cells(&self) -> &[(Glyph, Archetype)] {
|
||||||
|
&self.cells
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns `true` if the cell at `(x, y)` allows entities to pass through.
|
/// Returns `true` if the cell at `(x, y)` allows entities to pass through.
|
||||||
///
|
///
|
||||||
/// Objects block movement before the grid cell archetype is consulted.
|
/// Objects block movement before the grid cell archetype is consulted.
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/// Core game types: [`game::Board`], [`game::Glyph`], [`game::Archetype`], etc.
|
||||||
|
pub mod game;
|
||||||
|
/// Map file loading and saving (`.toml` format).
|
||||||
|
pub mod map_file;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::game::{Archetype, Board, FontSpec, Glyph, ObjectDef, Player, PortalDef};
|
use crate::game::{Archetype, Board, FontSpec, Glyph, ObjectDef, Player, PortalDef};
|
||||||
use eframe::egui::Color32;
|
use color::Rgba8;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
@@ -26,7 +26,7 @@ pub struct MapFile {
|
|||||||
pub font: Option<FontHeader>,
|
pub font: Option<FontHeader>,
|
||||||
/// Any `[[objects]]` entries. Optional; defaults to empty.
|
/// Any `[[objects]]` entries. Optional; defaults to empty.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub objects: Vec<MapFileObjectEntry>,
|
pub(crate) objects: Vec<MapFileObjectEntry>,
|
||||||
/// Any `[[portals]]` entries. Optional; defaults to empty.
|
/// Any `[[portals]]` entries. Optional; defaults to empty.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub portals: Vec<PortalDef>,
|
pub portals: Vec<PortalDef>,
|
||||||
@@ -73,7 +73,7 @@ pub struct FontHeader {
|
|||||||
/// the key to `tile`; the char form keeps working.
|
/// the key to `tile`; the char form keeps working.
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum TileIndex {
|
pub(crate) enum TileIndex {
|
||||||
/// A direct tile index (e.g. `tile = 35`).
|
/// A direct tile index (e.g. `tile = 35`).
|
||||||
Num(u32),
|
Num(u32),
|
||||||
/// A single-character shorthand (e.g. `tile = "#"`); converted to its Unicode scalar.
|
/// A single-character shorthand (e.g. `tile = "#"`); converted to its Unicode scalar.
|
||||||
@@ -81,7 +81,7 @@ enum TileIndex {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TileIndex {
|
impl TileIndex {
|
||||||
fn into_u32(self) -> u32 {
|
pub(crate) fn into_u32(self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
TileIndex::Num(n) => n,
|
TileIndex::Num(n) => n,
|
||||||
TileIndex::Chr(c) => c as u32,
|
TileIndex::Chr(c) => c as u32,
|
||||||
@@ -143,22 +143,22 @@ pub(crate) struct MapFileObjectEntry {
|
|||||||
script_name: Option<String>,
|
script_name: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses an `"#RRGGBB"` hex color string into a [`Color32`].
|
/// Parses an `"#RRGGBB"` hex color string into an [`Rgba8`].
|
||||||
/// Returns black on any parse failure.
|
/// Returns opaque black on any parse failure.
|
||||||
fn parse_color(hex: &str) -> Color32 {
|
fn parse_color(hex: &str) -> Rgba8 {
|
||||||
let hex = hex.trim_start_matches('#');
|
let hex = hex.trim_start_matches('#');
|
||||||
if hex.len() != 6 {
|
if hex.len() != 6 {
|
||||||
return Color32::BLACK;
|
return Rgba8 { r: 0, g: 0, b: 0, a: 255 };
|
||||||
}
|
}
|
||||||
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
|
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(0);
|
||||||
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
|
let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(0);
|
||||||
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
|
let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(0);
|
||||||
Color32::from_rgb(r, g, b)
|
Rgba8 { r, g, b, a: 255 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a [`Color32`] to an `"#RRGGBB"` hex string.
|
/// Converts an [`Rgba8`] to an `"#RRGGBB"` hex string (alpha is ignored).
|
||||||
fn color_to_hex(color: Color32) -> String {
|
fn color_to_hex(color: Rgba8) -> String {
|
||||||
format!("#{:02X}{:02X}{:02X}", color.r(), color.g(), color.b())
|
format!("#{:02X}{:02X}{:02X}", color.r, color.g, color.b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a parsed map file into a runtime [`Board`].
|
/// Converts a parsed map file into a runtime [`Board`].
|
||||||
@@ -368,6 +368,7 @@ impl From<&Board> for MapFile {
|
|||||||
script_name: o.script_name.clone(),
|
script_name: o.script_name.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let portals = board
|
let portals = board
|
||||||
.portals
|
.portals
|
||||||
.iter()
|
.iter()
|
||||||
@@ -498,8 +499,6 @@ O
|
|||||||
#[test]
|
#[test]
|
||||||
fn object_glyph_round_trips_through_toml() {
|
fn object_glyph_round_trips_through_toml() {
|
||||||
// Objects must survive a save→load cycle with their glyph intact.
|
// Objects must survive a save→load cycle with their glyph intact.
|
||||||
use eframe::egui::Color32;
|
|
||||||
|
|
||||||
let toml = r##"
|
let toml = r##"
|
||||||
[map]
|
[map]
|
||||||
name = "Test"
|
name = "Test"
|
||||||
@@ -531,8 +530,8 @@ bg = "#000000"
|
|||||||
assert_eq!(obj.x, 1);
|
assert_eq!(obj.x, 1);
|
||||||
assert_eq!(obj.y, 1);
|
assert_eq!(obj.y, 1);
|
||||||
assert_eq!(obj.glyph.tile, 64);
|
assert_eq!(obj.glyph.tile, 64);
|
||||||
assert_eq!(obj.glyph.fg, Color32::from_rgb(0x00, 0xFF, 0xFF));
|
assert_eq!(obj.glyph.fg, Rgba8 { r: 0x00, g: 0xFF, b: 0xFF, a: 255 });
|
||||||
assert_eq!(obj.glyph.bg, Color32::BLACK);
|
assert_eq!(obj.glyph.bg, Rgba8 { r: 0, g: 0, b: 0, a: 255 });
|
||||||
|
|
||||||
// Save back to TOML and reload; glyph must survive.
|
// Save back to TOML and reload; glyph must survive.
|
||||||
let map_file = MapFile::from(&board);
|
let map_file = MapFile::from(&board);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "kiln-egui"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "kiln"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
kiln-core = { path = "../kiln-core" }
|
||||||
|
eframe = "0.33.3"
|
||||||
|
color = "0.3.3"
|
||||||
|
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||||
|
rfd = "0.15"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -3,8 +3,8 @@ use egui::{Sense, vec2};
|
|||||||
|
|
||||||
use crate::font::BitmapFont;
|
use crate::font::BitmapFont;
|
||||||
use crate::font_dialog::{FontDialogState, show as show_font_dialog};
|
use crate::font_dialog::{FontDialogState, show as show_font_dialog};
|
||||||
use crate::game::{ALL_ARCHETYPES, Archetype, Board, Glyph};
|
|
||||||
use crate::render::{PALETTE_PANEL_W, paint_glyph};
|
use crate::render::{PALETTE_PANEL_W, paint_glyph};
|
||||||
|
use kiln_core::game::{ALL_ARCHETYPES, Archetype, Board, Glyph};
|
||||||
|
|
||||||
/// Which tab is active in the editor side panel.
|
/// Which tab is active in the editor side panel.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
@@ -2,7 +2,7 @@ use eframe::egui;
|
|||||||
use egui::{Color32, DragValue, Rect, Stroke, vec2};
|
use egui::{Color32, DragValue, Rect, Stroke, vec2};
|
||||||
|
|
||||||
use crate::font::BitmapFont;
|
use crate::font::BitmapFont;
|
||||||
use crate::game::FontSpec;
|
use kiln_core::game::FontSpec;
|
||||||
|
|
||||||
/// State for the font-picker dialog.
|
/// State for the font-picker dialog.
|
||||||
///
|
///
|
||||||
@@ -4,8 +4,8 @@ use eframe::egui;
|
|||||||
use egui::{Color32, Rect, Sense, Stroke, vec2};
|
use egui::{Color32, Rect, Sense, Stroke, vec2};
|
||||||
|
|
||||||
use crate::font::BitmapFont;
|
use crate::font::BitmapFont;
|
||||||
use crate::game::{Archetype, Glyph};
|
use crate::render::{paint_glyph, pos_to_cell, rgba8_to_color32};
|
||||||
use crate::render::{paint_glyph, pos_to_cell};
|
use kiln_core::game::{Archetype, Glyph};
|
||||||
|
|
||||||
/// Renders the floating glyph-picker dialog and updates `glyph` in place.
|
/// Renders the floating glyph-picker dialog and updates `glyph` in place.
|
||||||
///
|
///
|
||||||
@@ -62,18 +62,24 @@ pub(crate) fn show(
|
|||||||
|
|
||||||
ui.label("Colors");
|
ui.label("Colors");
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
// The color picker works with Color32; convert to/from Rgba8 around the call.
|
||||||
|
let mut fg = rgba8_to_color32(glyph.fg);
|
||||||
ui.label("FG");
|
ui.label("FG");
|
||||||
egui::color_picker::color_edit_button_srgba(
|
egui::color_picker::color_edit_button_srgba(
|
||||||
ui,
|
ui,
|
||||||
&mut glyph.fg,
|
&mut fg,
|
||||||
egui::color_picker::Alpha::Opaque,
|
egui::color_picker::Alpha::Opaque,
|
||||||
);
|
);
|
||||||
|
glyph.fg = color::Rgba8 { r: fg.r(), g: fg.g(), b: fg.b(), a: fg.a() };
|
||||||
|
|
||||||
|
let mut bg = rgba8_to_color32(glyph.bg);
|
||||||
ui.label("BG");
|
ui.label("BG");
|
||||||
egui::color_picker::color_edit_button_srgba(
|
egui::color_picker::color_edit_button_srgba(
|
||||||
ui,
|
ui,
|
||||||
&mut glyph.bg,
|
&mut bg,
|
||||||
egui::color_picker::Alpha::Opaque,
|
egui::color_picker::Alpha::Opaque,
|
||||||
);
|
);
|
||||||
|
glyph.bg = color::Rgba8 { r: bg.r(), g: bg.g(), b: bg.b(), a: bg.a() };
|
||||||
});
|
});
|
||||||
|
|
||||||
ui.separator();
|
ui.separator();
|
||||||
@@ -5,14 +5,13 @@ use std::path::{Path, PathBuf};
|
|||||||
mod editor;
|
mod editor;
|
||||||
mod font;
|
mod font;
|
||||||
mod font_dialog;
|
mod font_dialog;
|
||||||
mod game;
|
|
||||||
mod glyph_picker;
|
mod glyph_picker;
|
||||||
mod map_file;
|
|
||||||
mod render;
|
mod render;
|
||||||
|
|
||||||
use editor::{EditorState, EditorTab, show_editor_panel};
|
use editor::{EditorState, EditorTab, show_editor_panel};
|
||||||
use font::BitmapFont;
|
use font::BitmapFont;
|
||||||
use game::{Archetype, FontSpec, GameState, Glyph, ObjectDef};
|
use kiln_core::game::{Archetype, FontSpec, GameState, Glyph, ObjectDef};
|
||||||
|
use kiln_core::map_file;
|
||||||
use render::{
|
use render::{
|
||||||
DEFAULT_WINDOW_H, DEFAULT_WINDOW_W, MIN_WINDOW_H, MIN_WINDOW_W, board_origin, draw_board,
|
DEFAULT_WINDOW_H, DEFAULT_WINDOW_W, MIN_WINDOW_H, MIN_WINDOW_W, board_origin, draw_board,
|
||||||
draw_object_overlays, pos_to_cell,
|
draw_object_overlays, pos_to_cell,
|
||||||
@@ -56,7 +55,7 @@ struct App {
|
|||||||
state: GameState,
|
state: GameState,
|
||||||
mode: AppMode,
|
mode: AppMode,
|
||||||
editor: EditorState,
|
editor: EditorState,
|
||||||
/// The default font, loaded from `assets/default_font.png` or a placeholder.
|
/// The default font, embedded in the binary and loaded at startup.
|
||||||
default_font: BitmapFont,
|
default_font: BitmapFont,
|
||||||
/// A per-board font loaded from `board.font`, if one is specified.
|
/// A per-board font loaded from `board.font`, if one is specified.
|
||||||
board_font: Option<BitmapFont>,
|
board_font: Option<BitmapFont>,
|
||||||
@@ -71,9 +70,10 @@ impl App {
|
|||||||
/// `egui_ctx` is needed to upload font textures before the first frame.
|
/// `egui_ctx` is needed to upload font textures before the first frame.
|
||||||
/// `initial_path` is the file the board was loaded from and becomes the
|
/// `initial_path` is the file the board was loaded from and becomes the
|
||||||
/// default Save target.
|
/// default Save target.
|
||||||
fn new(board: game::Board, egui_ctx: &egui::Context, initial_path: PathBuf) -> Self {
|
fn new(board: kiln_core::game::Board, egui_ctx: &egui::Context, initial_path: PathBuf) -> Self {
|
||||||
// Load the default font from assets/. Fall back to a placeholder if missing.
|
// Default font is embedded in the binary; no runtime file I/O needed.
|
||||||
let default_font = BitmapFont::load(egui_ctx, Path::new("assets/vga-font-8x16.png"), 8, 16)
|
let font_bytes = include_bytes!("../assets/vga-font-8x16.png");
|
||||||
|
let default_font = BitmapFont::from_bytes(egui_ctx, font_bytes, 8, 16, "vga-font-8x16")
|
||||||
.unwrap_or_else(|_| BitmapFont::create_placeholder(egui_ctx));
|
.unwrap_or_else(|_| BitmapFont::create_placeholder(egui_ctx));
|
||||||
|
|
||||||
// If the board specifies its own font, try to load it now.
|
// If the board specifies its own font, try to load it now.
|
||||||
@@ -286,7 +286,7 @@ impl eframe::App for App {
|
|||||||
ctx,
|
ctx,
|
||||||
&mut self.editor.glyph_picker_open,
|
&mut self.editor.glyph_picker_open,
|
||||||
&mut self.editor.glyph,
|
&mut self.editor.glyph,
|
||||||
&self.state.board.cells,
|
&self.state.board.cells(),
|
||||||
font,
|
font,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -299,7 +299,7 @@ impl eframe::App for App {
|
|||||||
ctx,
|
ctx,
|
||||||
&mut self.editor.object_glyph_picker_open,
|
&mut self.editor.object_glyph_picker_open,
|
||||||
&mut self.editor.object_editing_glyph,
|
&mut self.editor.object_editing_glyph,
|
||||||
&self.state.board.cells,
|
&self.state.board.cells(),
|
||||||
font,
|
font,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,12 @@ use eframe::egui;
|
|||||||
use egui::{Color32, Pos2, Rect, vec2};
|
use egui::{Color32, Pos2, Rect, vec2};
|
||||||
|
|
||||||
use crate::font::BitmapFont;
|
use crate::font::BitmapFont;
|
||||||
use crate::game::{Board, Glyph, Player};
|
use kiln_core::game::{Board, Glyph, Player};
|
||||||
|
|
||||||
|
/// Converts a core [`Glyph`] color ([`color::Rgba8`]) to egui [`Color32`].
|
||||||
|
pub(crate) fn rgba8_to_color32(c: color::Rgba8) -> Color32 {
|
||||||
|
Color32::from_rgba_unmultiplied(c.r, c.g, c.b, c.a)
|
||||||
|
}
|
||||||
|
|
||||||
/// Approximate height of the egui menu bar in pixels, used for window sizing.
|
/// Approximate height of the egui menu bar in pixels, used for window sizing.
|
||||||
pub(crate) const MENU_H: f32 = 24.0;
|
pub(crate) const MENU_H: f32 = 24.0;
|
||||||
@@ -51,9 +56,9 @@ pub(crate) const MIN_WINDOW_H: f32 = 8.0 * 5.0 + MENU_H + 2.0 * PANEL_MARGIN;
|
|||||||
/// `glyph.fg`. Unlike [`draw_glyph`], the caller owns the rect and handles
|
/// `glyph.fg`. Unlike [`draw_glyph`], the caller owns the rect and handles
|
||||||
/// interaction — use this when you need the click [`egui::Response`].
|
/// interaction — use this when you need the click [`egui::Response`].
|
||||||
pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph, font: &BitmapFont) {
|
pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph, font: &BitmapFont) {
|
||||||
painter.rect_filled(rect, 0.0, glyph.bg);
|
painter.rect_filled(rect, 0.0, rgba8_to_color32(glyph.bg));
|
||||||
let uv = font.tile_uv(glyph.tile);
|
let uv = font.tile_uv(glyph.tile);
|
||||||
painter.image(font.texture.id(), rect, uv, glyph.fg);
|
painter.image(font.texture.id(), rect, uv, rgba8_to_color32(glyph.fg));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a single cell at grid position `(x, y)` using the given glyph and font.
|
/// Draws a single cell at grid position `(x, y)` using the given glyph and font.
|
||||||
Reference in New Issue
Block a user