use crate::prelude::{
init_raw, BTerm, CharacterTranslationMode, FlexiConsole, Font, InitHints, SimpleConsole,
SparseConsole, SpriteConsole, SpriteSheet, INPUT,
};
use crate::Result;
use bracket_color::prelude::RGB;
use std::collections::HashMap;
use std::convert::*;
struct BuilderFont {
path: String,
dimensions: (u32, u32),
explicit_background: Option<RGB>,
}
enum ConsoleType {
SimpleConsole {
width: u32,
height: u32,
font: String,
translator: CharacterTranslationMode,
},
SimpleConsoleNoBg {
width: u32,
height: u32,
font: String,
translator: CharacterTranslationMode,
},
SparseConsole {
width: u32,
height: u32,
font: String,
translator: CharacterTranslationMode,
},
SparseConsoleNoBg {
width: u32,
height: u32,
font: String,
translator: CharacterTranslationMode,
},
FlexiConsole {
width: u32,
height: u32,
font: String,
translator: CharacterTranslationMode,
},
SpriteConsole {
width: u32,
height: u32,
sprite_sheet: usize,
},
}
pub struct BTermBuilder {
width: u32,
height: u32,
title: Option<String>,
resource_path: String,
fonts: Vec<BuilderFont>,
consoles: Vec<ConsoleType>,
tile_width: u32,
tile_height: u32,
platform_hints: InitHints,
advanced_input: bool,
sprite_sheets: Vec<SpriteSheet>,
}
impl Default for BTermBuilder {
fn default() -> Self {
Self {
width: 80,
height: 50,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 8,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
}
}
}
impl BTermBuilder {
pub fn new() -> Self {
Self {
width: 80,
height: 50,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 8,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
}
}
pub fn simple80x50() -> Self {
let mut cb = Self {
width: 80,
height: 50,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 8,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
};
cb.fonts.push(BuilderFont {
path: "terminal8x8.png".to_string(),
dimensions: (8, 8),
explicit_background: None,
});
cb.consoles.push(ConsoleType::SimpleConsole {
width: 80,
height: 50,
font: "terminal8x8.png".to_string(),
translator: CharacterTranslationMode::Codepage437,
});
cb
}
pub fn simple<T>(width: T, height: T) -> Result<Self>
where
T: TryInto<u32>,
{
let w: u32 = width.try_into().or(Err("Must be convertible to a u32"))?;
let h: u32 = height.try_into().or(Err("Must be convertible to a u32"))?;
let mut cb = Self {
width: w,
height: h,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 8,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
};
cb.fonts.push(BuilderFont {
path: "terminal8x8.png".to_string(),
dimensions: (8, 8),
explicit_background: None,
});
cb.consoles.push(ConsoleType::SimpleConsole {
width: w,
height: h,
font: "terminal8x8.png".to_string(),
translator: CharacterTranslationMode::Codepage437,
});
Ok(cb)
}
pub fn vga80x50() -> Self {
let mut cb = Self {
width: 80,
height: 50,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 16,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
};
cb.fonts.push(BuilderFont {
path: "vga8x16.png".to_string(),
dimensions: (8, 8),
explicit_background: None,
});
cb.consoles.push(ConsoleType::SimpleConsole {
width: 80,
height: 50,
font: "vga8x16.png".to_string(),
translator: CharacterTranslationMode::Codepage437,
});
cb
}
pub fn vga<T>(width: T, height: T) -> Self
where
T: TryInto<u32>,
{
let w: u32 = width.try_into().ok().expect("Must be convertible to a u32");
let h: u32 = height
.try_into()
.ok()
.expect("Must be convertible to a u32");
let mut cb = Self {
width: w,
height: h,
title: None,
resource_path: "resources".to_string(),
fonts: Vec::new(),
consoles: Vec::new(),
tile_height: 16,
tile_width: 8,
platform_hints: InitHints::new(),
advanced_input: false,
sprite_sheets: Vec::new(),
};
cb.fonts.push(BuilderFont {
path: "vga8x16.png".to_string(),
dimensions: (8, 8),
explicit_background: None,
});
cb.consoles.push(ConsoleType::SimpleConsole {
width: w,
height: h,
font: "vga8x16.png".to_string(),
translator: CharacterTranslationMode::Codepage437,
});
cb
}
pub fn with_dimensions<T>(mut self, width: T, height: T) -> Self
where
T: TryInto<u32>,
{
self.width = width.try_into().ok().expect("Must be convertible to a u32");
self.height = height
.try_into()
.ok()
.expect("Must be convertible to a u32");
self
}
pub fn with_tile_dimensions<T>(mut self, width: T, height: T) -> Self
where
T: TryInto<u32>,
{
self.tile_width = width.try_into().ok().expect("Must be convertible to a u32");
self.tile_height = height
.try_into()
.ok()
.expect("Must be convertible to a u32");
self
}
pub fn with_title<S: ToString>(mut self, title: S) -> Self {
self.title = Some(title.to_string());
self
}
pub fn with_resource_path<S: ToString>(mut self, path: S) -> Self {
self.resource_path = path.to_string();
self
}
pub fn with_font<S: ToString, T>(mut self, font_path: S, width: T, height: T) -> Self
where
T: TryInto<u32>,
{
self.fonts.push(BuilderFont {
path: font_path.to_string(),
dimensions: (
width.try_into().ok().expect("Must be convertible to a u32"),
height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
),
explicit_background: None,
});
self
}
pub fn with_font_bg<S: ToString, T, COLOR>(
mut self,
font_path: S,
width: T,
height: T,
background: COLOR,
) -> Self
where
T: TryInto<u32>,
COLOR: Into<RGB>,
{
self.fonts.push(BuilderFont {
path: font_path.to_string(),
dimensions: (
width.try_into().ok().expect("Must be convertible to a u32"),
height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
),
explicit_background: Some(background.into()),
});
self
}
pub fn with_simple_console<S: ToString, T>(mut self, width: T, height: T, font: S) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::SimpleConsole {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
font: font.to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
pub fn with_simple_console_no_bg<S: ToString, T>(mut self, width: T, height: T, font: S) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::SimpleConsoleNoBg {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
font: font.to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
pub fn with_simple8x8(mut self) -> Self {
self.consoles.push(ConsoleType::SimpleConsole {
width: self.width,
height: self.height,
font: "terminal8x8.png".to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
pub fn with_sparse_console<S: ToString, T>(mut self, width: T, height: T, font: S) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::SparseConsole {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
font: font.to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
pub fn with_sparse_console_no_bg<S: ToString, T>(mut self, width: T, height: T, font: S) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::SparseConsoleNoBg {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
font: font.to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
#[cfg(feature = "opengl")]
pub fn with_fancy_console<S: ToString, T>(mut self, width: T, height: T, font: S) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::FlexiConsole {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
font: font.to_string(),
translator: CharacterTranslationMode::Codepage437,
});
self
}
#[cfg(feature = "opengl")]
pub fn with_sprite_console<T>(mut self, width: T, height: T, sprite_sheet: usize) -> Self
where
T: TryInto<u32>,
{
self.consoles.push(ConsoleType::SpriteConsole {
width: width.try_into().ok().expect("Must be convertible to a u32"),
height: height
.try_into()
.ok()
.expect("Must be convertible to a u32"),
sprite_sheet,
});
self
}
pub fn with_vsync(mut self, vsync: bool) -> Self {
self.platform_hints.vsync = vsync;
self
}
pub fn with_fullscreen(mut self, fullscreen: bool) -> Self {
self.platform_hints.fullscreen = fullscreen;
self
}
pub fn with_platform_specific(mut self, hints: InitHints) -> Self {
self.platform_hints = hints;
self
}
pub fn with_fps_cap(mut self, fps: f32) -> Self {
self.platform_hints.frame_sleep_time = Some(1.0 / fps);
self
}
pub fn with_advanced_input(mut self, advanced_input: bool) -> Self {
self.advanced_input = advanced_input;
self
}
#[cfg(all(feature = "opengl", not(target_arch = "wasm32")))]
pub fn with_automatic_console_resize(mut self, resize_scaling: bool) -> Self {
self.platform_hints.resize_scaling = resize_scaling;
self
}
#[cfg(feature = "opengl")]
pub fn with_sprite_sheet(mut self, ss: SpriteSheet) -> Self {
self.sprite_sheets.push(ss);
self
}
pub fn build(self) -> Result<BTerm> {
let mut context = init_raw(
self.width * self.tile_width,
self.height * self.tile_height,
self.title.unwrap_or_else(|| "BTerm Window".to_string()),
self.platform_hints,
)?;
let mut font_map: HashMap<String, usize> = HashMap::new();
for font in &self.fonts {
let font_path = format!("{}/{}", self.resource_path, font.path);
let font_id = context.register_font(Font::load(
font_path.clone(),
font.dimensions,
font.explicit_background,
));
font_map.insert(font_path, font_id?);
}
#[cfg(feature = "opengl")]
for ss in self.sprite_sheets {
context.register_spritesheet(ss);
}
for console in &self.consoles {
match console {
ConsoleType::SimpleConsole {
width,
height,
font,
translator,
} => {
let font_path = format!("{}/{}", self.resource_path, font);
let font_id = font_map[&font_path];
let cid =
context.register_console(SimpleConsole::init(*width, *height), font_id);
context.set_translation_mode(cid, *translator);
}
ConsoleType::SimpleConsoleNoBg {
width,
height,
font,
translator,
} => {
let font_path = format!("{}/{}", self.resource_path, font);
let font_id = font_map[&font_path];
let cid = context
.register_console_no_bg(SimpleConsole::init(*width, *height), font_id);
context.set_translation_mode(cid, *translator);
}
ConsoleType::SparseConsole {
width,
height,
font,
translator,
} => {
let font_path = format!("{}/{}", self.resource_path, font);
let font_id = font_map[&font_path];
let cid =
context.register_console(SparseConsole::init(*width, *height), font_id);
context.set_translation_mode(cid, *translator);
}
ConsoleType::SparseConsoleNoBg {
width,
height,
font,
translator,
} => {
let font_path = format!("{}/{}", self.resource_path, font);
let font_id = font_map[&font_path];
let cid = context
.register_console_no_bg(SparseConsole::init(*width, *height), font_id);
context.set_translation_mode(cid, *translator);
}
ConsoleType::FlexiConsole {
width,
height,
font,
translator,
} => {
let font_path = format!("{}/{}", self.resource_path, font);
let font_id = font_map[&font_path];
let cid = context
.register_fancy_console(FlexiConsole::init(*width, *height), font_id);
context.set_translation_mode(cid, *translator);
}
ConsoleType::SpriteConsole {
width,
height,
sprite_sheet,
} => {
context.register_sprite_console(SpriteConsole::init(
*width,
*height,
*sprite_sheet,
));
}
}
}
if self.advanced_input {
INPUT.lock().activate_event_queue();
}
Ok(context)
}
}