+ 'a {
+ assert!(title.as_ref().is_ascii());
+ self.title = Box::new(title);
+ self
+ }
+
+ pub fn fullscreen(&mut self, is_fullscreen: bool) -> &mut RootInitializer<'a> {
+ self.is_fullscreen = is_fullscreen;
+ self
+ }
+
+ pub fn font(&mut self, path: P, font_layout: FontLayout) -> &mut RootInitializer<'a> where P: AsRef + 'a {
+ self.font_path = Box::new(path);
+ self.font_layout = font_layout;
+ self
+ }
+
+ pub fn font_type(&mut self, font_type: FontType) -> &mut RootInitializer<'a> {
+ self.font_type = font_type;
+ self
+ }
+
+ pub fn font_dimensions(&mut self, horizontal: i32, vertical: i32) -> &mut RootInitializer<'a> {
+ self.font_dimensions = (horizontal, vertical);
+ self
+ }
+
+ pub fn renderer(&mut self, renderer: Renderer) -> &mut RootInitializer<'a> {
+ self.console_renderer = renderer;
+ self
+ }
+
+ pub fn init(&self) -> Root {
+ assert!(self.width > 0 && self.height > 0);
+
+ match self.font_dimensions {
+ (horizontal, vertical) => {
+ Root::set_custom_font((*self.font_path).as_ref(),
+ self.font_layout, self.font_type,
+ horizontal, vertical)
+ }
+ }
+
+ unsafe {
+ let c_title = CString::new((*self.title).as_ref().as_bytes()).unwrap();
+ ffi::TCOD_console_init_root(self.width, self.height,
+ c_title.as_ptr(),
+ self.is_fullscreen as c_bool,
+ self.console_renderer as u32);
+ }
+ Root { _blocker: PhantomData }
+ }
+}
+
+pub trait TcodString {
+ fn as_ascii(&self) -> Option<&[u8]>;
+}
+
+impl TcodString for str {
+ fn as_ascii(&self) -> Option<&[u8]> {
+ match self.is_ascii() {
+ true => Some(self.as_ref()),
+ false => None,
+ }
+ }
+}
+
+impl<'a> TcodString for &'a str {
+ fn as_ascii(&self) -> Option<&[u8]> {
+ (*self).as_ascii()
+ }
+}
+
+impl TcodString for String {
+ fn as_ascii(&self) -> Option<&[u8]> {
+ AsRef::::as_ref(self).as_ascii()
+ }
+}
+
+impl<'a> TcodString for &'a String {
+ fn as_ascii(&self) -> Option<&[u8]> {
+ AsRef::::as_ref(self).as_ascii()
+ }
+}
+
+pub trait AsciiLiteral {}
+impl AsciiLiteral for [u8] {}
+
+// AsciiLiteral is implemented for fixed-size arrays up to length 32, same as the current
+// Rust standard library trait implementation for fixed-size arrays (13 June 2015)
+impl AsciiLiteral for [u8; 0] {}
+impl AsciiLiteral for [u8; 1] {}
+impl AsciiLiteral for [u8; 2] {}
+impl AsciiLiteral for [u8; 3] {}
+impl AsciiLiteral for [u8; 4] {}
+impl AsciiLiteral for [u8; 5] {}
+impl AsciiLiteral for [u8; 6] {}
+impl AsciiLiteral for [u8; 7] {}
+impl AsciiLiteral for [u8; 8] {}
+impl AsciiLiteral for [u8; 9] {}
+impl AsciiLiteral for [u8; 10] {}
+impl AsciiLiteral for [u8; 11] {}
+impl AsciiLiteral for [u8; 12] {}
+impl AsciiLiteral for [u8; 13] {}
+impl AsciiLiteral for [u8; 14] {}
+impl AsciiLiteral for [u8; 15] {}
+impl AsciiLiteral for [u8; 16] {}
+impl AsciiLiteral for [u8; 17] {}
+impl AsciiLiteral for [u8; 18] {}
+impl AsciiLiteral for [u8; 19] {}
+impl AsciiLiteral for [u8; 20] {}
+impl AsciiLiteral for [u8; 21] {}
+impl AsciiLiteral for [u8; 22] {}
+impl AsciiLiteral for [u8; 23] {}
+impl AsciiLiteral for [u8; 24] {}
+impl AsciiLiteral for [u8; 25] {}
+impl AsciiLiteral for [u8; 26] {}
+impl AsciiLiteral for [u8; 27] {}
+impl AsciiLiteral for [u8; 28] {}
+impl AsciiLiteral for [u8; 29] {}
+impl AsciiLiteral for [u8; 30] {}
+impl AsciiLiteral for [u8; 31] {}
+impl AsciiLiteral for [u8; 32] {}
+
+impl<'a, T> AsciiLiteral for &'a T where T: AsciiLiteral {}
+
+impl TcodString for T where T: AsRef<[u8]> + AsciiLiteral {
+ fn as_ascii(&self) -> Option<&[u8]> {
+ Some(self.as_ref())
+ }
+}
+
+#[inline]
+fn to_wstring(text: &[u8]) -> Vec {
+ let mut ret = str::from_utf8(text).unwrap().chars().collect::>();
+ ret.push('\0');
+ ret
+}
+
+/// Defines the common functionality between `Root` and `Offscreen` consoles
+///
+/// # Examples
+/// Printing text with explicit alignment:
+///
+/// ```no_run
+/// use tcod::console::{Console, Root, BackgroundFlag, TextAlignment};
+///
+/// let mut root = Root::initializer().size(80, 50).init();
+///
+/// root.print_ex(1, 1, BackgroundFlag::None, TextAlignment::Left,
+/// "Text aligned to left.");
+///
+/// root.print_ex(78, 1, BackgroundFlag::None, TextAlignment::Right,
+/// "Text aligned to right.");
+///
+/// root.print_ex(40, 15, BackgroundFlag::None, TextAlignment::Center,
+/// "And this bit of text is centered.");
+///
+/// root.print_ex(40, 19, BackgroundFlag::None, TextAlignment::Center,
+/// "Press any key to quit.");
+/// ```
+pub trait Console : AsNative {
+ /// Returns the default text alignment for the `Console` instance. For all the possible
+ /// text alignment options, see the documentation for
+ /// [TextAlignment](./enum.TextAlignment.html).
+ fn get_alignment(&self) -> TextAlignment {
+ let alignment = unsafe {
+ ffi::TCOD_console_get_alignment(*self.as_native())
+ };
+ unsafe { transmute(alignment) }
+ }
+
+ /// Sets the default text alignment for the console. For all the possible
+ /// text alignment options, see the documentation for
+ /// [TextAlignment](./enum.TextAlignment.html).
+ fn set_alignment(&mut self, alignment: TextAlignment) {
+ unsafe {
+ ffi::TCOD_console_set_alignment(*self.as_native(), alignment as u32);
+ }
+ }
+
+ /// Sets a key color that will be ignored when [blitting](./fn.blit.html) the contents
+ /// of this console onto an other (essentially a transparent background color).
+ fn set_key_color(&mut self, color: Color) {
+ unsafe {
+ ffi::TCOD_console_set_key_color(*self.as_native(), *color.as_native());
+ }
+ }
+
+ /// Returns the width of the console in characters.
+ fn width(&self) -> i32 {
+ unsafe {
+ ffi::TCOD_console_get_width(*self.as_native())
+ }
+ }
+
+ /// Returns the height of the console in characters.
+ fn height(&self) -> i32 {
+ unsafe {
+ ffi::TCOD_console_get_height(*self.as_native())
+ }
+ }
+
+ /// Sets the console's default background color. This is used in several other methods,
+ /// like: `clear`, `put_char`, etc.
+ fn set_default_background(&mut self, color: Color) {
+ unsafe {
+ ffi::TCOD_console_set_default_background(*self.as_native(), *color.as_native());
+ }
+ }
+
+ /// Sets the console's default foreground color. This is used in several printing functions.
+ fn set_default_foreground(&mut self, color: Color) {
+ unsafe {
+ ffi::TCOD_console_set_default_foreground(*self.as_native(), *color.as_native());
+ }
+ }
+
+ /// Returns the background color of the cell at the specified coordinates.
+ fn get_char_background(&self, x: i32, y: i32) -> Color {
+ unsafe {
+ FromNative::from_native(
+ ffi::TCOD_console_get_char_background(*self.as_native(), x, y))
+ }
+ }
+
+ /// Returns the foreground color of the cell at the specified coordinates.
+ fn get_char_foreground(&self, x: i32, y: i32) -> Color {
+ unsafe {
+ FromNative::from_native(
+ ffi::TCOD_console_get_char_foreground(*self.as_native(), x, y))
+ }
+ }
+
+ /// Returns the console's current background flag. For a detailed explanation
+ /// of the possible values, see [BackgroundFlag](./enum.BackgroundFlag.html).
+ fn get_background_flag(&self) -> BackgroundFlag {
+ let flag = unsafe {
+ ffi::TCOD_console_get_background_flag(*self.as_native())
+ };
+ unsafe { transmute(flag) }
+ }
+
+ /// Sets the console's current background flag. For a detailed explanation
+ /// of the possible values, see [BackgroundFlag](./enum.BackgroundFlag.html).
+ fn set_background_flag(&mut self, background_flag: BackgroundFlag) {
+ unsafe {
+ ffi::TCOD_console_set_background_flag(*self.as_native(),
+ background_flag as u32);
+ }
+ }
+
+ /// Returns the ASCII value of the cell located at `x, y`
+ fn get_char(&self, x: i32, y: i32) -> char {
+ let ffi_char = unsafe {
+ ffi::TCOD_console_get_char(*self.as_native(), x, y)
+ };
+ assert!(ffi_char >= 0 && ffi_char < 256);
+ ffi_char as u8 as char
+ }
+
+ /// Modifies the ASCII value of the cell located at `x, y`.
+ fn set_char(&mut self, x: i32, y: i32, c: char) {
+ assert!(x >= 0 && y >= 0);
+ unsafe {
+ ffi::TCOD_console_set_char(*self.as_native(), x, y, c as i32)
+ }
+ }
+
+ /// Changes the background color of the specified cell
+ fn set_char_background(&mut self, x: i32, y: i32,
+ color: Color,
+ background_flag: BackgroundFlag) {
+ assert!(x >= 0 && y >= 0);
+ unsafe {
+ ffi::TCOD_console_set_char_background(*self.as_native(),
+ x, y,
+ *color.as_native(),
+ background_flag as u32)
+ }
+ }
+
+ /// Changes the foreground color of the specified cell
+ fn set_char_foreground(&mut self, x: i32, y: i32, color: Color) {
+ assert!(x >= 0 && y >= 0);
+ unsafe {
+ ffi::TCOD_console_set_char_foreground(*self.as_native(),
+ x, y,
+ *color.as_native());
+ }
+ }
+
+ /// This function modifies every property of the given cell:
+ ///
+ /// 1. Updates its background color according to the console's default and `background_flag`,
+ /// see [BackgroundFlag](./enum.BackgroundFlag.html).
+ /// 2. Updates its foreground color based on the default color set in the console
+ /// 3. Sets its ASCII value to `glyph`
+ fn put_char(&mut self,
+ x: i32, y: i32, glyph: char,
+ background_flag: BackgroundFlag) {
+ assert!(x >= 0 && y >= 0);
+ unsafe {
+ ffi::TCOD_console_put_char(*self.as_native(),
+ x, y, glyph as i32,
+ background_flag as u32);
+ }
+ }
+
+ /// Updates every propert of the given cell using explicit colors for the
+ /// background and foreground.
+ fn put_char_ex(&mut self,
+ x: i32, y: i32, glyph: char,
+ foreground: Color, background: Color) {
+ assert!(x >= 0 && y >= 0);
+ unsafe {
+ ffi::TCOD_console_put_char_ex(*self.as_native(),
+ x, y, glyph as i32,
+ *foreground.as_native(),
+ *background.as_native());
+ }
+ }
+
+ /// Clears the console with its default background color
+ fn clear(&mut self) {
+ unsafe {
+ ffi::TCOD_console_clear(*self.as_native());
+ }
+ }
+
+ /// Prints the text at the specified location. The position of the `x` and `y`
+ /// coordinates depend on the [TextAlignment](./enum.TextAlignment.html) set in the console:
+ ///
+ /// * `TextAlignment::Left`: leftmost character of the string
+ /// * `TextAlignment::Center`: center character of the sting
+ /// * `TextAlignment::Right`: rightmost character of the string
+ fn print(&mut self, x: i32, y: i32, text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
+ assert!(x >= 0 && y >= 0);
+ if let Some(text) = text.as_ascii() {
+ let c_text = CString::new(text).unwrap();
+ unsafe {
+ ffi::TCOD_console_print(*self.as_native(), x, y, c_text.as_ptr());
+ }
+ } else {
+ let c_text = to_wstring(text.as_ref());
+ unsafe {
+ ffi::TCOD_console_print_utf(*self.as_native(), x, y, c_text.as_ptr() as *const i32);
+ }
+ }
+ }
+
+ /// Prints the text at the specified location in a rectangular area with
+ /// the dimensions: (width; height). If the text is longer than the width the
+ /// newlines will be inserted.
+ fn print_rect(&mut self,
+ x: i32, y: i32,
+ width: i32, height: i32,
+ text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
+ assert!(x >= 0 && y >= 0);
+ if let Some(text) = text.as_ascii() {
+ let c_text = CString::new(text).unwrap();
+ unsafe {
+ ffi::TCOD_console_print_rect(*self.as_native(), x, y, width, height, c_text.as_ptr());
+ }
+ } else {
+ let c_text = to_wstring(text.as_ref());
+ unsafe {
+ ffi::TCOD_console_print_rect_utf(*self.as_native(), x, y, width, height, c_text.as_ptr() as *const i32);
+ }
+ }
+ }
+
+ /// Prints the text at the specified location with an explicit
+ /// [BackgroundFlag](./enum.BackgroundFlag.html) and
+ /// [TextAlignment](./enum.TextAlignment.html).
+ fn print_ex(&mut self,
+ x: i32, y: i32,
+ background_flag: BackgroundFlag,
+ alignment: TextAlignment,
+ text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
+ assert!(x >= 0 && y >= 0);
+ if let Some(text) = text.as_ascii() {
+ let c_text = CString::new(text).unwrap();
+ unsafe {
+ ffi::TCOD_console_print_ex(*self.as_native(), x, y,
+ background_flag as u32,
+ alignment as u32,
+ c_text.as_ptr());
+ }
+ } else {
+ let c_text = to_wstring(text.as_ref());
+ unsafe {
+ ffi::TCOD_console_print_ex_utf(*self.as_native(), x, y,
+ background_flag as u32,
+ alignment as u32,
+ c_text.as_ptr() as *const i32);
+ }
+ }
+ }
+
+ /// Combines the functions of `print_ex` and `print_rect`
+ fn print_rect_ex(&mut self,
+ x: i32, y: i32,
+ width: i32, height: i32,
+ background_flag: BackgroundFlag,
+ alignment: TextAlignment,
+ text: T) where Self: Sized, T: AsRef<[u8]> + TcodString {
+ assert!(x >= 0 && y >= 0);
+ if let Some(text) = text.as_ascii() {
+ let c_text = CString::new(text).unwrap();
+ unsafe {
+ ffi::TCOD_console_print_rect_ex(*self.as_native(), x, y, width, height,
+ background_flag as u32, alignment as u32,
+ c_text.as_ptr());
+ }
+ } else {
+ let c_text = to_wstring(text.as_ref());
+ unsafe {
+ ffi::TCOD_console_print_rect_ex_utf(*self.as_native(), x, y, width, height,
+ background_flag as u32, alignment as u32,
+ c_text.as_ptr() as *const i32);
+ }
+ }
+ }
+
+ /// Compute the height of a wrapped text printed using `print_rect` or `print_rect_ex`.
+ fn get_height_rect(&self,
+ x: i32, y: i32,
+ width: i32, height: i32,
+ text: T) -> i32 where Self: Sized, T: AsRef<[u8]> + TcodString {
+ assert!(x >= 0 && y >= 0);
+ if let Some(text) = text.as_ascii() {
+ let c_text = CString::new(text).unwrap();
+ unsafe {
+ ffi::TCOD_console_get_height_rect(*self.as_native(), x, y, width, height,
+ c_text.as_ptr())
+ }
+ } else {
+ let c_text = to_wstring(text.as_ref());
+ unsafe {
+ ffi::TCOD_console_get_height_rect_utf(*self.as_native(), x, y, width, height,
+ c_text.as_ptr() as *const i32)
+ }
+ }
+ }
+
+
+ /// Fill a rectangle with the default background colour.
+ ///
+ /// If `clear` is true, set each cell's character to space (ASCII 32).
+ fn rect(&mut self,
+ x: i32, y: i32,
+ width: i32, height: i32,
+ clear: bool,
+ background_flag: BackgroundFlag) {
+ assert!(x >= 0 && y >= 0 && width >= 0 && height >= 0);
+ assert!(x + width < self.width() && y + height < self.height());
+ unsafe {
+ ffi::TCOD_console_rect(*self.as_native(), x, y, width, height, clear as c_bool, background_flag as u32);
+ }
+ }
+
+ /// Draw a horizontal line.
+ ///
+ /// Uses `tcod::chars::HLINE` (ASCII 196) as the line character and
+ /// console's default background and foreground colours.
+ fn horizontal_line(&mut self, x: i32, y: i32, length: i32, background_flag: BackgroundFlag) {
+ assert!(x >= 0 && y >= 0 && y < self.height());
+ assert!(length >= 1 && length + x < self.width());
+ unsafe {
+ ffi::TCOD_console_hline(*self.as_native(), x, y, length, background_flag as u32);
+ }
+ }
+
+ /// Draw a vertical line.
+ ///
+ /// Uses `tcod::chars::VLINE` (ASCII 179) as the line character and
+ /// console's default background and foreground colours.
+ fn vertical_line(&mut self, x: i32, y: i32, length: i32, background_flag: BackgroundFlag) {
+ assert!(x >= 0, y >= 0 && x < self.width());
+ assert!(length >= 1 && length + y < self.height());
+ unsafe {
+ ffi::TCOD_console_vline(*self.as_native(), x, y, length, background_flag as u32);
+ }
+ }
+
+ /// Draw a window frame with an optional title.
+ ///
+ /// Draws a rectangle (using the rect method) using the suplied background
+ /// flag, then draws a rectangle with the console's default foreground
+ /// colour.
+ ///
+ /// If the `title` is specified, it will be printed on top of the rectangle
+ /// using inverted colours.
+ fn print_frame(&mut self, x: i32, y: i32, width: i32, height: i32,
+ clear: bool, background_flag: BackgroundFlag, title: Option) where Self: Sized, T: AsRef {
+ assert!(x >= 0 && y >= 0 && width >= 0 && height >= 0);
+ assert!(x + width <= self.width() && y + height <= self.height());
+ let c_title: *const c_char = match title {
+ Some(s) => {
+ assert!(s.as_ref().is_ascii());
+ CString::new(s.as_ref()).unwrap().as_ptr()
+ },
+ None => ptr::null(),
+ };
+ unsafe {
+ ffi::TCOD_console_print_frame(*self.as_native(), x, y, width, height,
+ clear as c_bool, background_flag as u32, c_title);
+ }
+ }
+}
+
+/// Blits the contents of one console onto an other
+///
+/// It takes a region from a given console (with an arbitrary location, width and height) and superimposes
+/// it on the destination console (at the given location).
+/// Note that when blitting, the source console's key color (set by `set_key_color`) will
+/// be ignored, making it possible to blit non-rectangular regions.
+///
+/// # Arguments
+///
+/// * `source_console`: the type implementing the [Console](./trait.Console.html) trait we want to
+/// take the blitted region from
+/// * `source_x`, `source_y`: the coordinates of the blitted region's top left corner on the source
+/// console
+/// * `source_width`, `source_height`: the width and height of the blitted region. With a value of
+/// 0, the width and height of the source console will be used.
+/// * `destination_console`: the type implementing the [Console](./trait.Console.html) trait we want
+/// to blit to
+/// * `destination_x`, `destination_y`: the coordinated of the blitted region's top left corner on
+/// the destination console
+/// * `foreground_alpha`, `background_alpha`: the foreground and background opacity
+///
+/// # Examples
+///
+/// Using `blit` with concrete types and `Console` trait objects:
+///
+/// ```no_run
+/// use tcod::console as console;
+/// use tcod::console::{Console, Root, Offscreen};
+///
+/// fn main() {
+/// let mut root = Root::initializer().init();
+///
+/// let mut direct = Offscreen::new(20, 20);
+/// let mut boxed_direct = Box::new(Offscreen::new(20, 20));
+/// let mut trait_object: &Console = &Offscreen::new(20, 20);
+/// let mut boxed_trait: Box = Box::new(Offscreen::new(20, 20));
+///
+/// console::blit(&direct, (0, 0), (20, 20), &mut root, (0, 0), 1.0, 1.0);
+/// console::blit(&boxed_direct, (0, 0), (20, 20), &mut root, (20, 0), 1.0, 1.0);
+/// console::blit(&trait_object, (0, 0), (20, 20), &mut root, (0, 20), 1.0, 1.0);
+/// console::blit(&boxed_trait, (0, 0), (20, 20), &mut root, (20, 20), 1.0, 1.0);
+/// }
+///
+/// ```
+pub fn blit(source_console: &T,
+ (source_x, source_y): (i32, i32),
+ (source_width, source_height): (i32, i32),
+ destination_console: &mut U,
+ (destination_x, destination_y): (i32, i32),
+ foreground_alpha: f32, background_alpha: f32)
+ where T: Console,
+ U: Console {
+ assert!(source_x >= 0 && source_y >= 0 &&
+ source_width >= 0 && source_height >= 0); // If width or height is 0, the source width/height is used.
+
+ unsafe {
+ ffi::TCOD_console_blit(*source_console.as_native(),
+ source_x, source_y, source_width, source_height,
+ *destination_console.as_native(),
+ destination_x, destination_y,
+ foreground_alpha, background_alpha);
+ }
+}
+
+impl<'a, T: Console + ?Sized> Console for &'a T {}
+
+impl Console for Box {}
+
+impl AsNative for Root {
+ unsafe fn as_native(&self) -> &ffi::TCOD_console_t {
+ &ROOT_ID.id
+ }
+}
+
+impl AsNative for Offscreen {
+ unsafe fn as_native(&self) -> &ffi::TCOD_console_t {
+ &self.con
+ }
+}
+
+impl Console for Root {}
+impl Console for Offscreen {}
+
+/// Represents the text alignment in console instances.
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum TextAlignment {
+ Left = ffi::TCOD_LEFT as isize,
+ Right = ffi::TCOD_RIGHT as isize,
+ Center = ffi::TCOD_CENTER as isize,
+}
+
+/// This flag determines how a cell's existing background color will be modified by a new one
+///
+/// See [libtcod's documentation](http://doryen.eptalys.net/data/libtcod/doc/1.5.2/html2/console_bkgnd_flag_t.html)
+/// for a detailed description of the different values.
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub enum BackgroundFlag {
+ None = ffi::TCOD_BKGND_NONE as isize,
+ Set = ffi::TCOD_BKGND_SET as isize,
+ Multiply = ffi::TCOD_BKGND_MULTIPLY as isize,
+ Lighten = ffi::TCOD_BKGND_LIGHTEN as isize,
+ Darken = ffi::TCOD_BKGND_DARKEN as isize,
+ Screen = ffi::TCOD_BKGND_SCREEN as isize,
+ ColorDodge = ffi::TCOD_BKGND_COLOR_DODGE as isize,
+ ColorBurn = ffi::TCOD_BKGND_COLOR_BURN as isize,
+ Add = ffi::TCOD_BKGND_ADD as isize,
+ AddA = ffi::TCOD_BKGND_ADDA as isize,
+ Burn = ffi::TCOD_BKGND_BURN as isize,
+ Overlay = ffi::TCOD_BKGND_OVERLAY as isize,
+ Alph = ffi::TCOD_BKGND_ALPH as isize,
+ Default = ffi::TCOD_BKGND_DEFAULT as isize
+}
+
+/// All the possible renderers used by the `Root` console
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum Renderer {
+ GLSL = ffi::TCOD_RENDERER_GLSL as isize,
+ OpenGL = ffi::TCOD_RENDERER_OPENGL as isize,
+ SDL = ffi::TCOD_RENDERER_SDL as isize,
+}
+
+/// All the possible font layouts that can be used for custom bitmap fonts
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum FontLayout {
+ AsciiInCol = ffi::TCOD_FONT_LAYOUT_ASCII_INCOL as isize,
+ AsciiInRow = ffi::TCOD_FONT_LAYOUT_ASCII_INROW as isize,
+ Tcod = ffi::TCOD_FONT_LAYOUT_TCOD as isize,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum FontType {
+ Default = 0,
+ Greyscale = ffi::TCOD_FONT_TYPE_GREYSCALE as isize,
+}
+
+
+
+#[cfg(test)]
+mod test {
+ use std::path::Path;
+ use super::Root;
+ use super::FontLayout::AsciiInCol;
+
+ #[test]
+ fn test_custom_font_as_static_str() {
+ Root::initializer().font("terminal.png", AsciiInCol);
+ }
+
+ #[test]
+ fn test_custom_font_as_path() {
+ Root::initializer().font(Path::new("terminal.png"), AsciiInCol);
+
+ }
+
+ #[test]
+ fn test_custom_font_as_string() {
+ Root::initializer().font("terminal.png".to_owned(), AsciiInCol);
+ }
+
+ #[test]
+ fn test_custom_font_as_str() {
+ let string = "terminal.png".to_owned();
+ let s: &str = &string;
+ Root::initializer().font(s, AsciiInCol);
+ }
+}
diff --git a/tcod/src/console_macros.rs b/tcod/src/console_macros.rs
new file mode 100644
index 00000000..5bcdd63b
--- /dev/null
+++ b/tcod/src/console_macros.rs
@@ -0,0 +1,106 @@
+#[macro_export]
+macro_rules! tcod_print {
+ // ABW
+ ($con: expr, At($x: expr, $y: expr), Align($alignment: expr),
+ Bg($bg: expr), Wrap($width: expr, $height: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // AWB
+ ($con: expr, At($x: expr, $y: expr), Align($alignment: expr),
+ Wrap($width: expr, $height: expr), Bg($bg: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // BAW
+ ($con: expr, At($x: expr, $y: expr), Bg($bg: expr),
+ Align($alignment: expr), Wrap($width: expr, $height: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // BWA
+ ($con: expr, At($x: expr, $y: expr), Bg($bg: expr),
+ Wrap($width: expr, $height: expr), Align($alignment: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // WAB
+ ($con: expr, At($x: expr, $y: expr), Wrap($width: expr, $height: expr),
+ Align($alignment: expr), Bg($bg: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // WBA
+ ($con: expr, At($x: expr, $y: expr), Wrap($width: expr, $height: expr),
+ Bg($bg: expr), Align($alignment: expr), $($arg: tt)*) => (
+ $con.print_rect_ex($x, $y, $width, $height, $bg, $alignment, format!($($arg)*));
+ );
+
+ // AB
+ ($con: expr, At($x: expr, $y: expr), Align($bg: expr), Bg($alignment: expr), $($arg: tt)*) => (
+ $con.print_ex($x, $y, $bg, $alignment, format!($($arg)*));
+ );
+
+ // AW
+ ($con: expr, At($x: expr, $y: expr), Align($alignment: expr), Wrap($width: expr, $height: expr), $($arg: tt)*) => (
+ {
+ let bg = $con.get_background_flag();
+ $con.print_rect_ex($x, $y, $width, $height, bg, $alignment, format!($($arg)*));
+ }
+ );
+
+ // BA
+ ($con: expr, At($x: expr, $y: expr), Bg($bg: expr), Align($alignment: expr), $($arg: tt)*) => (
+ $con.print_ex($x, $y, $bg, $alignment, format!($($arg)*));
+ );
+
+ // BW
+ ($con: expr, At($x: expr, $y: expr), Bg($bg: expr), Wrap($width: expr, $height: expr), $($arg: tt)*) => (
+ {
+ let alignment = $con.get_alignment();
+ $con.print_rect_ex($x, $y, $width, $height, $bg, alignment, format!($($arg)*));
+ }
+ );
+
+ // WA
+ ($con: expr, At($x: expr, $y: expr), Wrap($width: expr, $height: expr), Align($alignment: expr), $($arg: tt)*) => (
+ {
+ let bg = $con.get_background_flag();
+ $con.print_rect_ex($x, $y, $width, $height, bg, $alignment, format!($($arg)*));
+ }
+ );
+
+ // WB
+ ($con: expr, At($x: expr, $y: expr), Wrap($width: expr, $height: expr), Bg($bg: expr), $($arg: tt)*) => (
+ {
+ let alignment = $con.get_alignment();
+ $con.print_rect_ex($x, $y, $width, $height, $bg, alignment, format!($($arg)*));
+ }
+ );
+
+ // A
+ ($con: expr, At($x: expr, $y: expr), Align($alignment: expr), $($arg: tt)*) => (
+ {
+ let bg = $con.get_background_flag();
+ $con.print_ex($x, $y, bg, $alignment, format!($($arg)*));
+ }
+ );
+
+ // B
+ ($con: expr, At($x: expr, $y: expr), Bg($bg: expr), $($arg: tt)*) => (
+ {
+ let alignment = $con.get_alignment();
+ $con.print_ex($x, $y, $bg, alignment, format!($($arg)*));
+ }
+ );
+
+ // W
+ ($con: expr, At($x: expr, $y: expr), Wrap($width: expr, $height: expr), $($arg: tt)*) => (
+ $con.print_rect($x, $y, $width, $height, format!($($arg)*));
+ );
+
+ // None
+ ($con: expr, At($x: expr, $y: expr), $($arg: tt)*) => (
+ $con.print($x, $y, format!($($arg)*));
+ );
+}
diff --git a/tcod/src/image.rs b/tcod/src/image.rs
new file mode 100644
index 00000000..c9dd0175
--- /dev/null
+++ b/tcod/src/image.rs
@@ -0,0 +1,218 @@
+use std::io::{Error, ErrorKind};
+use std::path::Path;
+
+use bindings::ffi;
+use bindings::{AsNative, FromNative, CString};
+use colors::Color;
+use console::{BackgroundFlag, Console};
+
+pub struct Image {
+ tcod_image: ffi::TCOD_image_t,
+ width: i32,
+ height: i32,
+}
+
+impl AsNative for Image {
+ unsafe fn as_native(&self) -> &ffi::TCOD_image_t {
+ &self.tcod_image
+ }
+}
+
+impl FromNative for Image {
+ unsafe fn from_native(image: ffi::TCOD_image_t) -> Image {
+ let (width, height) = get_image_size(image);
+ assert!(width != 0);
+ Image { tcod_image: image, width: width, height: height }
+ }
+}
+
+#[inline]
+unsafe fn get_image_size(tcod_image: ffi::TCOD_image_t) -> (i32, i32) {
+ let (mut width, mut height) = (0, 0);
+ ffi::TCOD_image_get_size(tcod_image, &mut width, &mut height);
+ (width, height)
+}
+
+impl Image {
+ pub fn new(width: i32, height: i32) -> Image {
+ unsafe {
+ Image {
+ tcod_image: ffi::TCOD_image_new(width, height),
+ width: width,
+ height: height,
+ }
+ }
+ }
+
+ pub fn from_file(path: T) -> Result where T: AsRef {
+ let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap();
+ unsafe {
+ let tcod_image = ffi::TCOD_image_load(path_string.as_ptr());
+ let (width, height) = get_image_size(tcod_image);
+
+ if width == 0 {
+ Err(Error::new(ErrorKind::InvalidInput, "The provided image format is not supported by libtcod"))
+ } else {
+ Ok(Image { tcod_image: tcod_image, width: width, height: height })
+ }
+ }
+ }
+
+ pub fn from_console(console: &T) -> Image where T: Console {
+ unsafe {
+ let tcod_image = ffi::TCOD_image_from_console(*console.as_native());
+ let (width, height) = get_image_size(tcod_image);
+ Image {
+ tcod_image: tcod_image,
+ width: width,
+ height: height
+ }
+ }
+ }
+
+ pub fn refresh_console(&mut self, console: &T) where T: Console {
+ assert!(
+ {
+ let img = Image::from_console(console);
+ self.width == img.width && self.height == img.height
+ },
+
+ "libtcod only supports console refreshing with consoles of equivalent sizes"
+ );
+
+ unsafe {
+ ffi::TCOD_image_refresh_console(self.tcod_image, *console.as_native());
+ }
+ }
+
+ pub fn save(&self, path: T) where T: AsRef {
+ let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap();
+ unsafe {
+ ffi::TCOD_image_save(self.tcod_image, path_string.as_ptr());
+ }
+ }
+
+ pub fn width(&self) -> i32 {
+ self.width
+ }
+
+ pub fn height(&self) -> i32 {
+ self.height
+ }
+
+ pub fn get_size(&self) -> (i32, i32) {
+ (self.width, self.height)
+ }
+
+ pub fn get_pixel(&self, x: i32, y: i32) -> Color {
+ assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
+ unsafe {
+ FromNative::from_native(ffi::TCOD_image_get_pixel(self.tcod_image, x, y))
+ }
+ }
+
+ pub fn get_alpha(&self, x: i32, y: i32) -> i32 {
+ assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
+ unsafe {
+ ffi::TCOD_image_get_alpha(self.tcod_image, x, y)
+ }
+ }
+
+ pub fn is_pixel_transparent(&self, x: i32, y: i32) -> bool {
+ assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
+ unsafe {
+ ffi::TCOD_image_is_pixel_transparent(self.tcod_image, x, y) != 0
+ }
+ }
+
+ pub fn get_mipmap_pixel(&self, (x0, y0): (f32, f32), (x1, y1): (f32, f32)) -> Color {
+ assert!(x0 >= 0.0 && y0 >= 0.0 &&
+ x0 < x1 && y0 < y1 &&
+ x1 < self.width as f32 && y1 < self.height as f32);
+ unsafe {
+ FromNative::from_native(ffi::TCOD_image_get_mipmap_pixel(self.tcod_image, x0, y0, x1, y1))
+ }
+ }
+
+ pub fn set_key_color(&mut self, color: Color) {
+ unsafe {
+ ffi::TCOD_image_set_key_color(self.tcod_image, *color.as_native());
+ }
+ }
+
+ pub fn clear(&mut self, color: Color) {
+ unsafe {
+ ffi::TCOD_image_clear(self.tcod_image, *color.as_native());
+ }
+ }
+
+ pub fn put_pixel(&mut self, x: i32, y: i32, color: Color) {
+ assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
+ unsafe {
+ ffi::TCOD_image_put_pixel(self.tcod_image, x, y, *color.as_native());
+ }
+ }
+
+ pub fn scale(&mut self, width: i32, height: i32) {
+ unsafe {
+ ffi::TCOD_image_scale(self.tcod_image, width, height);
+ }
+ self.width = width;
+ self.height = height;
+ }
+
+ pub fn hflip(&mut self) {
+ unsafe {
+ ffi::TCOD_image_hflip(self.tcod_image);
+ }
+ }
+
+ pub fn vflip(&mut self) {
+ unsafe {
+ ffi::TCOD_image_vflip(self.tcod_image);
+ }
+ }
+
+ pub fn rotate90(&mut self, num_rotations: i32) {
+ let (width, height) = unsafe {
+ ffi::TCOD_image_rotate90(self.tcod_image, num_rotations);
+ get_image_size(self.tcod_image)
+ };
+ self.width = width;
+ self.height = height;
+ }
+
+ pub fn invert(&mut self) {
+ unsafe {
+ ffi::TCOD_image_invert(self.tcod_image);
+ }
+ }
+}
+
+pub fn blit_rect(src: &Image, (width, height): (i32, i32),
+ dst: &mut T, (x, y): (i32, i32), flag: BackgroundFlag) where T: Console {
+ assert!(width >= -1 && height >= -1 && width <= src.width && height <= src.height);
+ assert!(x >= 0 && y >= 0 && x < dst.width() && y < dst.height());
+ unsafe {
+ ffi::TCOD_image_blit_rect(src.tcod_image, *dst.as_native(), x, y, width, height, flag as u32);
+ }
+}
+
+pub fn blit(src: &Image, (scale_x, scale_y): (f32, f32), angle: f32,
+ dst: &mut T, (x, y): (f32, f32), flag: BackgroundFlag) where T: Console {
+ assert!(scale_x > 0.0 && scale_y > 0.0);
+ assert!(x >= 0.0 && y >= 0.0 && x < dst.width() as f32 && y < dst.height() as f32);
+ unsafe {
+ ffi::TCOD_image_blit(src.tcod_image, *dst.as_native(), x, y, flag as u32, scale_x, scale_y, angle);
+ }
+}
+
+pub fn blit_2x(src: &Image, (src_x, src_y): (i32, i32), (width, height): (i32, i32),
+ dst: &mut T, (dst_x, dst_y): (i32, i32)) where T: Console {
+ assert!(width >= -1 && height >= -1 && width <= src.width && height <= src.height);
+ assert!(src_x >= 0 && src_y >= 0 && src_x < src.width && src_y < src.height);
+ assert!(dst_x >= 0 && dst_y >= 0 && dst_x < dst.width() && dst_y < dst.height());
+ unsafe {
+ ffi::TCOD_image_blit_2x(src.tcod_image, *dst.as_native(), dst_x, dst_y, src_x, src_y, width, height);
+ }
+}
diff --git a/tcod/src/input.rs b/tcod/src/input.rs
new file mode 100644
index 00000000..3e3b6e18
--- /dev/null
+++ b/tcod/src/input.rs
@@ -0,0 +1,255 @@
+use std::mem;
+
+use bindings::ffi;
+use bindings::{c_bool, c_uint, keycode_from_u32};
+
+
+/// Deprecated. Use `tcod::input::Mouse` instead.
+pub type MouseState = Mouse;
+
+#[repr(C)]
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum KeyCode {
+ NoKey = ffi::TCODK_NONE as isize,
+ Escape = ffi::TCODK_ESCAPE as isize,
+ Backspace = ffi::TCODK_BACKSPACE as isize,
+ Tab = ffi::TCODK_TAB as isize,
+ Enter = ffi::TCODK_ENTER as isize,
+ Shift = ffi::TCODK_SHIFT as isize,
+ Control = ffi::TCODK_CONTROL as isize,
+ Alt = ffi::TCODK_ALT as isize,
+ Pause = ffi::TCODK_PAUSE as isize,
+ CapsLock = ffi::TCODK_CAPSLOCK as isize,
+ PageUp = ffi::TCODK_PAGEUP as isize,
+ PageDown = ffi::TCODK_PAGEDOWN as isize,
+ End = ffi::TCODK_END as isize,
+ Home = ffi::TCODK_HOME as isize,
+ Up = ffi::TCODK_UP as isize,
+ Left = ffi::TCODK_LEFT as isize,
+ Right = ffi::TCODK_RIGHT as isize,
+ Down = ffi::TCODK_DOWN as isize,
+ PrintScreen = ffi::TCODK_PRINTSCREEN as isize,
+ Insert = ffi::TCODK_INSERT as isize,
+ Delete = ffi::TCODK_DELETE as isize,
+ LeftWin = ffi::TCODK_LWIN as isize,
+ RightWin = ffi::TCODK_RWIN as isize,
+ Apps = ffi::TCODK_APPS as isize,
+ // The numbers on the alphanum section of the keyboard
+ Number0 = ffi::TCODK_0 as isize,
+ Number1 = ffi::TCODK_1 as isize,
+ Number2 = ffi::TCODK_2 as isize,
+ Number3 = ffi::TCODK_3 as isize,
+ Number4 = ffi::TCODK_4 as isize,
+ Number5 = ffi::TCODK_5 as isize,
+ Number6 = ffi::TCODK_6 as isize,
+ Number7 = ffi::TCODK_7 as isize,
+ Number8 = ffi::TCODK_8 as isize,
+ Number9 = ffi::TCODK_9 as isize,
+ // The numbers on the numeric keypad
+ NumPad0 = ffi::TCODK_KP0 as isize,
+ NumPad1 = ffi::TCODK_KP1 as isize,
+ NumPad2 = ffi::TCODK_KP2 as isize,
+ NumPad3 = ffi::TCODK_KP3 as isize,
+ NumPad4 = ffi::TCODK_KP4 as isize,
+ NumPad5 = ffi::TCODK_KP5 as isize,
+ NumPad6 = ffi::TCODK_KP6 as isize,
+ NumPad7 = ffi::TCODK_KP7 as isize,
+ NumPad8 = ffi::TCODK_KP8 as isize,
+ NumPad9 = ffi::TCODK_KP9 as isize,
+ NumPadAdd = ffi::TCODK_KPADD as isize,
+ NumPadSubtract = ffi::TCODK_KPSUB as isize,
+ NumPadDivide = ffi::TCODK_KPDIV as isize,
+ NumPadMultiply = ffi::TCODK_KPMUL as isize,
+ NumPadDecimal = ffi::TCODK_KPDEC as isize,
+ NumPadEnter = ffi::TCODK_KPENTER as isize,
+ F1 = ffi::TCODK_F1 as isize,
+ F2 = ffi::TCODK_F2 as isize,
+ F3 = ffi::TCODK_F3 as isize,
+ F4 = ffi::TCODK_F4 as isize,
+ F5 = ffi::TCODK_F5 as isize,
+ F6 = ffi::TCODK_F6 as isize,
+ F7 = ffi::TCODK_F7 as isize,
+ F8 = ffi::TCODK_F8 as isize,
+ F9 = ffi::TCODK_F9 as isize,
+ F10 = ffi::TCODK_F10 as isize,
+ F11 = ffi::TCODK_F11 as isize,
+ F12 = ffi::TCODK_F12 as isize,
+ NumLock = ffi::TCODK_NUMLOCK as isize,
+ ScrollLock = ffi::TCODK_SCROLLLOCK as isize,
+ Spacebar = ffi::TCODK_SPACE as isize,
+ Char = ffi::TCODK_CHAR as isize,
+}
+
+impl Default for KeyCode {
+ fn default() -> Self {
+ KeyCode::NoKey
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Default)]
+pub struct Key {
+ pub code: KeyCode,
+ pub printable: char,
+ pub pressed: bool,
+ pub left_alt: bool,
+ pub left_ctrl: bool,
+ pub right_alt: bool,
+ pub right_ctrl: bool,
+ pub shift: bool,
+ pub alt: bool,
+ pub ctrl: bool,
+}
+
+impl From for Key {
+ fn from(tcod_key: ffi::TCOD_key_t) -> Key {
+ Key {
+ code: keycode_from_u32(tcod_key.vk).unwrap(),
+ printable: tcod_key.c as u8 as char,
+ pressed: tcod_key.pressed != 0,
+ left_alt: tcod_key.lalt != 0,
+ left_ctrl: tcod_key.lctrl != 0,
+ right_alt: tcod_key.ralt != 0,
+ right_ctrl: tcod_key.rctrl != 0,
+ shift: tcod_key.shift != 0,
+ alt: tcod_key.lalt != 0 || tcod_key.ralt != 0,
+ ctrl: tcod_key.lctrl != 0 || tcod_key.rctrl != 0,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug, Default)]
+pub struct Mouse {
+ pub x: isize,
+ pub y: isize,
+ pub dx: isize,
+ pub dy: isize,
+ pub cx: isize,
+ pub cy: isize,
+ pub dcx: isize,
+ pub dcy: isize,
+ pub lbutton: bool,
+ pub rbutton: bool,
+ pub mbutton: bool,
+ pub lbutton_pressed: bool,
+ pub rbutton_pressed: bool,
+ pub mbutton_pressed: bool,
+ pub wheel_up: bool,
+ pub wheel_down: bool,
+}
+
+
+pub fn show_cursor(visible: bool) {
+ unsafe {
+ ffi::TCOD_mouse_show_cursor(visible as c_bool);
+ }
+}
+
+pub fn is_cursor_visible() -> bool {
+ unsafe {
+ ffi::TCOD_mouse_is_cursor_visible() != 0
+ }
+}
+
+pub fn move_cursor(x: i32, y: i32) {
+ unsafe {
+ ffi::TCOD_mouse_move(x, y);
+ }
+}
+
+bitflags! {
+ flags KeyPressFlags: c_uint {
+ const KEY_PRESSED = ffi::TCOD_KEY_PRESSED,
+ const KEY_RELEASED = ffi::TCOD_KEY_RELEASED,
+ }
+}
+
+bitflags! {
+ flags EventFlags: c_uint {
+ const KEY_PRESS = ffi::TCOD_EVENT_KEY_PRESS,
+ const KEY_RELEASE = ffi::TCOD_EVENT_KEY_RELEASE,
+ const KEY = ffi::TCOD_EVENT_KEY,
+ const MOUSE_MOVE = ffi::TCOD_EVENT_MOUSE_MOVE,
+ const MOUSE_PRESS = ffi::TCOD_EVENT_MOUSE_PRESS,
+ const MOUSE_RELEASE = ffi::TCOD_EVENT_MOUSE_RELEASE,
+ const MOUSE = ffi::TCOD_EVENT_MOUSE,
+ const ANY = ffi::TCOD_EVENT_ANY,
+ }
+}
+
+pub fn check_for_event(event_mask: EventFlags) -> Option<(EventFlags, Event)> {
+ let mut c_key_state: ffi::TCOD_key_t = unsafe { mem::uninitialized() };
+ let mut c_mouse_state: ffi::TCOD_mouse_t = unsafe { mem::uninitialized() };
+
+ let event = unsafe {
+ ffi::TCOD_sys_check_for_event(event_mask.bits() as i32,
+ &mut c_key_state, &mut c_mouse_state)
+ };
+
+ let ret_flag = match event {
+ ffi::TCOD_EVENT_KEY_PRESS => KEY_PRESS,
+ ffi::TCOD_EVENT_KEY_RELEASE => KEY_RELEASE,
+ ffi::TCOD_EVENT_KEY => KEY,
+ ffi::TCOD_EVENT_MOUSE => MOUSE,
+ ffi::TCOD_EVENT_MOUSE_MOVE => MOUSE_MOVE,
+ ffi::TCOD_EVENT_MOUSE_PRESS => MOUSE_PRESS,
+ ffi::TCOD_EVENT_MOUSE_RELEASE => MOUSE_RELEASE,
+ _ => ANY
+ };
+
+ if ret_flag == ANY {
+ return None
+ }
+
+ let ret_event = if ret_flag.intersects(KEY_PRESS|KEY_RELEASE|KEY) {
+ Some(Event::Key(c_key_state.into()))
+ } else if ret_flag.intersects(MOUSE_MOVE|MOUSE_PRESS|MOUSE_RELEASE|MOUSE) {
+ Some(Event::Mouse(Mouse {
+ x: c_mouse_state.x as isize,
+ y: c_mouse_state.y as isize,
+ dx: c_mouse_state.dx as isize,
+ dy: c_mouse_state.dy as isize,
+ cx: c_mouse_state.cx as isize,
+ cy: c_mouse_state.cy as isize,
+ dcx: c_mouse_state.dcx as isize,
+ dcy: c_mouse_state.dcy as isize,
+ lbutton: c_mouse_state.lbutton != 0,
+ rbutton: c_mouse_state.rbutton != 0,
+ mbutton: c_mouse_state.mbutton != 0,
+ lbutton_pressed: c_mouse_state.lbutton_pressed != 0,
+ rbutton_pressed: c_mouse_state.rbutton_pressed != 0,
+ mbutton_pressed: c_mouse_state.mbutton_pressed != 0,
+ wheel_up: c_mouse_state.wheel_up != 0,
+ wheel_down: c_mouse_state.wheel_down != 0
+ }))
+ } else {
+ None
+ };
+
+ ret_event.map(|event| (ret_flag, event))
+}
+
+pub fn events() -> EventIterator {
+ EventIterator::new()
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum Event {
+ Key(Key),
+ Mouse(Mouse)
+}
+
+pub struct EventIterator;
+
+impl EventIterator {
+ pub fn new() -> Self {
+ EventIterator
+ }
+}
+
+impl Iterator for EventIterator {
+ type Item = (EventFlags, Event);
+
+ fn next(&mut self) -> Option<(EventFlags, Event)> {
+ check_for_event(KEY | MOUSE)
+ }
+}
diff --git a/tcod/src/lib.rs b/tcod/src/lib.rs
new file mode 100644
index 00000000..6d5d99cb
--- /dev/null
+++ b/tcod/src/lib.rs
@@ -0,0 +1,74 @@
+//! libtcod bindings for Rust
+//!
+//! ## Description
+//! [libtcod a.k.a. "The Doryen Library"](http://roguecentral.org/doryen/libtcod/) is a
+//! free, fast, portable and uncomplicated API for roguelike developpers providing lots of
+//! useful functions, such as:
+//!
+//! * Text-based graphics API
+//! * Colors
+//! * Keyboard and mouse input
+//! * Path finding
+//! * Field of View
+//! * Portability (works on Windows, Linux and OS X)
+//!
+//! For the full set of features see the [libtcod features page](http://roguecentral.org/doryen/libtcod/features/).
+//!
+//! All raw bindings are available via the `tcod-sys` crate, however the `tcod-rs` library aims to
+//! provide safe, Rust-style wrappers for most of `libtcod`. These wrappers are not yet complete,
+//! however.
+//!
+//! ### Features already implemented:
+//!
+//! * Colors
+//! * Console
+//! * Most of the System layer
+//! * Field of View
+//! * Map
+//! * Pathfinding
+//! * Line toolkit
+//! * Noise
+//! * BSP Toolkit
+//!
+//! ### Features that are not planned to be implemented:
+//! This are features that Rust already provides a good (and in most casese more idiomatic)
+//! solution for:
+//!
+//! * Filesystem utilities
+//! * Containers
+//! * Pseudorandom generators
+//! * Compression utilities
+//!
+
+#[macro_use] extern crate bitflags;
+#[macro_use] extern crate lazy_static;
+#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
+#[cfg(feature = "serde")] extern crate serde;
+#[cfg(test)] extern crate serde_json;
+
+pub use bindings::{AsNative, FromNative};
+pub use colors::Color;
+pub use console::{Console, RootInitializer, BackgroundFlag, Renderer, FontLayout, FontType, TextAlignment};
+pub use map::Map;
+
+pub mod bsp;
+pub mod chars;
+pub mod colors;
+pub mod console;
+pub mod image;
+pub mod input;
+pub mod line;
+pub mod map;
+pub mod namegen;
+pub mod noise;
+pub mod pathfinding;
+pub mod random;
+pub mod system;
+
+mod bindings;
+mod console_macros;
+mod serde_impls;
+mod rustc_serialize_impls;
+
+pub type RootConsole = console::Root;
+pub type OffscreenConsole = console::Offscreen;
diff --git a/tcod/src/line.rs b/tcod/src/line.rs
new file mode 100644
index 00000000..78e25348
--- /dev/null
+++ b/tcod/src/line.rs
@@ -0,0 +1,162 @@
+//! Port of line drawing toolkit.
+
+extern crate libc;
+
+use bindings::ffi;
+use bindings::c_int;
+
+/// tcod-rs uses libtcod's multithreaded line API, therefore more then one line
+/// can be created and drawn. The `Line` struct represents a line.
+#[derive(Default)]
+pub struct Line {
+ tcod_line: ffi::TCOD_bresenham_data_t,
+}
+
+impl Line {
+ /// Creates a line from `start` to `end` (inclusive).
+ pub fn new(start: (i32, i32), end: (i32, i32)) -> Self {
+ let mut line: Line = Default::default();
+ unsafe {
+ ffi::TCOD_line_init_mt(start.0, start.1, end.0, end.1, &mut line.tcod_line)
+ };
+ line
+ }
+
+ /// Creates a new line and steps over it using provided closure as a callback.
+ /// The stepping is aborted when the closure returns false. The function returs
+ /// a part of the line that has not been stepped over.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # use tcod::line::Line;
+ /// let mut line = Line::new_with_callback((1, 1), (5, 5), |x, _y| {
+ /// x < 4
+ /// });
+ /// assert_eq!(Some((5, 5)), line.next());
+ /// ```
+ pub fn new_with_callback(start: (i32, i32), end: (i32, i32), callback: F) -> Self
+ where F: FnMut(i32, i32) -> bool
+ {
+ let mut line: Line = Line::new(start, end);
+ line.step_with_callback(callback);
+ line
+ }
+
+ /// You can step through each point of the line. Return `None` when end of line
+ /// has been reached.
+ pub fn step(&mut self) -> Option<(i32, i32)> {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ let end = unsafe {
+ ffi::TCOD_line_step_mt(&mut x, &mut y, &mut self.tcod_line)
+ };
+
+ if end == 0 {
+ Some((x, y))
+ } else {
+ None
+ }
+ }
+
+ fn step_with_callback(&mut self, mut callback: F) -> bool
+ where F: FnMut(i32, i32) -> bool
+ {
+ let mut x: c_int = self.tcod_line.origx;
+ let mut y: c_int = self.tcod_line.origy;
+ loop {
+ if !callback(x, y) {
+ return false
+ }
+ let step = unsafe {
+ ffi::TCOD_line_step_mt(&mut x, &mut y, &mut self.tcod_line)
+ };
+ if step != 0 {
+ break
+ }
+ }
+ true
+ }
+}
+
+impl Iterator for Line {
+ type Item = (i32, i32);
+
+ fn next(&mut self) -> Option {
+ self.step()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::Line;
+
+ #[test]
+ fn line_created() {
+ let line = Line::new((1, 1), (5, 5));
+
+ assert_eq!(line.tcod_line.origx, 1);
+ assert_eq!(line.tcod_line.origy, 1);
+ assert_eq!(line.tcod_line.destx, 5);
+ assert_eq!(line.tcod_line.desty, 5);
+ }
+
+ #[test]
+ fn start_end_same() {
+ let line = Line::new((1, 1), (1, 1));
+
+ assert_eq!(line.tcod_line.origx, 1);
+ assert_eq!(line.tcod_line.origy, 1);
+ assert_eq!(line.tcod_line.destx, 1);
+ assert_eq!(line.tcod_line.desty, 1);
+ }
+
+ #[test]
+ fn step_line() {
+ let mut line = Line::new((1, 1), (5, 5));
+
+ assert_eq!(Some((2, 2)), line.step());
+ assert_eq!(Some((3, 3)), line.step());
+ assert_eq!(Some((4, 4)), line.step());
+ assert_eq!(Some((5, 5)), line.step());
+ assert_eq!(None, line.step());
+ }
+
+ #[test]
+ fn step_two_lines() {
+ let mut line1 = Line::new((1, 1), (5, 5));
+ let mut line2 = Line::new((10, 10), (14, 14));
+
+ assert_eq!(Some((2, 2)), line1.step());
+ assert_eq!(Some((11, 11)), line2.step());
+ assert_eq!(Some((3, 3)), line1.step());
+ assert_eq!(Some((12, 12)), line2.step());
+ assert_eq!(Some((4, 4)), line1.step());
+ assert_eq!(Some((13, 13)), line2.step());
+ assert_eq!(Some((5, 5)), line1.step());
+ assert_eq!(Some((14, 14)), line2.step());
+ assert_eq!(None, line1.step());
+ assert_eq!(None, line2.step());
+ }
+
+ #[test]
+ fn iterate_over_line() {
+ let mut line = Line::new((1, 1), (5, 5));
+
+ assert_eq!(Some((2, 2)), line.next());
+ assert_eq!(Some((3, 3)), line.next());
+ assert_eq!(Some((4, 4)), line.next());
+ assert_eq!(Some((5, 5)), line.next());
+ assert_eq!(None, line.next());
+ }
+
+ #[test]
+ fn line_with_callback() {
+ let mut line = Line::new_with_callback((1, 1), (5, 5), |x, _y| {
+ assert!(x <= 4);
+ x < 4
+ });
+ assert_eq!(Some((5, 5)), line.next());
+ assert_eq!(None, line.next());
+ }
+}
diff --git a/tcod/src/map.rs b/tcod/src/map.rs
new file mode 100644
index 00000000..8c419576
--- /dev/null
+++ b/tcod/src/map.rs
@@ -0,0 +1,93 @@
+use bindings::ffi;
+use bindings::{AsNative, c_bool};
+
+pub struct Map {
+ tcod_map: ffi::TCOD_map_t,
+}
+
+impl AsNative for Map {
+ unsafe fn as_native(&self) -> &ffi::TCOD_map_t {
+ &self.tcod_map
+ }
+}
+
+impl Map {
+ pub fn new(width: i32, height: i32) -> Map {
+ assert!(width > 0 && height > 0);
+ unsafe {
+ Map{tcod_map: ffi::TCOD_map_new(width, height)}
+ }
+ }
+
+ pub fn size(&self) -> (i32, i32) {
+ unsafe {
+ (ffi::TCOD_map_get_width(self.tcod_map),
+ ffi::TCOD_map_get_height(self.tcod_map))
+ }
+ }
+
+ pub fn set(&mut self, x: i32, y: i32, transparent: bool, walkable: bool) {
+ assert!(x >= 0 && y >= 0);
+ let (width, height) = self.size();
+ assert!(x < width && y < height);
+ unsafe {
+ ffi::TCOD_map_set_properties(self.tcod_map, x, y,
+ transparent as c_bool,
+ walkable as c_bool);
+ }
+ }
+
+ pub fn compute_fov(&mut self, origin_x: i32, origin_y: i32, max_radius: i32,
+ light_walls: bool, algo: FovAlgorithm) {
+ assert!(origin_x >= 0 && origin_y >= 0);
+ unsafe {
+ ffi::TCOD_map_compute_fov(self.tcod_map, origin_x, origin_y, max_radius,
+ light_walls as c_bool,
+ algo as u32);
+ }
+ }
+
+ pub fn is_in_fov(&self, x: i32, y: i32) -> bool {
+ assert!(x >= 0 && y >= 0);
+ let (width, height) = self.size();
+ assert!(x < width && y < height);
+ unsafe {
+ ffi::TCOD_map_is_in_fov(self.tcod_map, x, y) != 0
+ }
+ }
+
+ pub fn is_walkable(&self, x: i32, y: i32) -> bool {
+ assert!(x >= 0 && y >= 0);
+ let (width, height) = self.size();
+ assert!(x < width && y < height);
+ unsafe {
+ ffi::TCOD_map_is_walkable(self.tcod_map, x, y) != 0
+ }
+ }
+}
+
+impl Drop for Map {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::TCOD_map_delete(self.tcod_map)
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Copy, Clone, Debug)]
+pub enum FovAlgorithm {
+ Basic = ffi::FOV_BASIC as isize,
+ Diamond = ffi::FOV_DIAMOND as isize,
+ Shadow = ffi::FOV_SHADOW as isize,
+ Permissive0 = ffi::FOV_PERMISSIVE_0 as isize,
+ Permissive1 = ffi::FOV_PERMISSIVE_1 as isize,
+ Permissive2 = ffi::FOV_PERMISSIVE_2 as isize,
+ Permissive3 = ffi::FOV_PERMISSIVE_3 as isize,
+ Permissive4 = ffi::FOV_PERMISSIVE_4 as isize,
+ Permissive5 = ffi::FOV_PERMISSIVE_5 as isize,
+ Permissive6 = ffi::FOV_PERMISSIVE_6 as isize,
+ Permissive7 = ffi::FOV_PERMISSIVE_7 as isize,
+ Permissive8 = ffi::FOV_PERMISSIVE_8 as isize,
+ Restrictive = ffi::FOV_RESTRICTIVE as isize,
+}
diff --git a/tcod/src/namegen.rs b/tcod/src/namegen.rs
new file mode 100644
index 00000000..8c8cad85
--- /dev/null
+++ b/tcod/src/namegen.rs
@@ -0,0 +1,107 @@
+use std::ptr;
+use std::str;
+use std::ffi::{CStr, CString};
+use std::path::Path;
+use std::sync::Mutex;
+
+use bindings::ffi;
+use bindings::{AsNative, c_char};
+use random::Rng;
+
+static mut NAMEGEN_FREE: bool = true;
+lazy_static! {
+ static ref NAMEGEN_MUTEX: Mutex<()> = Mutex::new(());
+}
+
+pub struct Namegen {
+ rng: Vec,
+}
+
+impl Drop for Namegen {
+ fn drop(&mut self) {
+ unsafe {
+ let _lock = NAMEGEN_MUTEX.lock()
+ .ok()
+ .expect("Namegen mutex could not be locked");
+ if self.rng.is_empty() {
+ ffi::TCOD_namegen_destroy();
+ }
+ NAMEGEN_FREE = true;
+ }
+ }
+}
+
+impl Namegen {
+ pub fn new() -> Option {
+ unsafe {
+ match NAMEGEN_FREE {
+ true => {
+ let _lock = NAMEGEN_MUTEX.lock()
+ .ok()
+ .expect("Namegen mutex could not be locked");
+ NAMEGEN_FREE = false;
+ Some(Namegen { rng: Vec::new() })
+ },
+ false => None
+ }
+ }
+ }
+
+ pub fn parse(&mut self, path: T) where T: AsRef {
+ self.parse_with_rng(path, &Rng::get_instance())
+ }
+
+ pub fn parse_with_rng(&mut self, path: T, rng: &Rng) where T: AsRef {
+ self.rng.push(rng.save());
+
+ let path_string = CString::new(path.as_ref().to_str().unwrap()).unwrap();
+ unsafe {
+ ffi::TCOD_namegen_parse(path_string.as_ptr(), *self.rng.last().unwrap().as_native());
+ }
+ }
+
+ pub fn generate(&self, name: T) -> Option where T: AsRef {
+ unsafe {
+ let name_string = CString::new(name.as_ref()).unwrap();
+ let borrowed = ffi::TCOD_namegen_generate(name_string.as_ptr() as *mut _, 0);
+ cstr_to_owned(borrowed)
+ }
+ }
+
+ pub fn generate_custom(&self, name: T, rule: U) -> Option where T: AsRef, U: AsRef {
+ unsafe {
+ let name_string = CString::new(name.as_ref()).unwrap();
+ let rule_string = CString::new(rule.as_ref()).unwrap();
+
+ let borrowed = ffi::TCOD_namegen_generate_custom(name_string.as_ptr() as *mut _,
+ rule_string.as_ptr() as *mut _, 0);
+ cstr_to_owned(borrowed)
+ }
+ }
+
+ pub fn get_sets(&self) -> Vec {
+ unsafe {
+ let list = ffi::TCOD_namegen_get_sets();
+ let size = ffi::TCOD_list_size(list);
+ let mut ret = Vec::with_capacity(size as usize);
+ for i in 0..size {
+ ret.push(cstr_to_owned(ffi::TCOD_list_get(list, i) as *mut c_char).unwrap());
+ }
+ ret
+ }
+ }
+}
+
+#[inline]
+fn cstr_to_owned(string: *mut c_char) -> Option {
+ if string == ptr::null::() as *mut _ {
+ return None;
+ }
+
+ unsafe {
+ let string = CStr::from_ptr(string);
+ str::from_utf8(string.to_bytes())
+ .map(|x| x.to_owned())
+ .ok()
+ }
+}
diff --git a/tcod/src/noise.rs b/tcod/src/noise.rs
new file mode 100644
index 00000000..ffb33b94
--- /dev/null
+++ b/tcod/src/noise.rs
@@ -0,0 +1,463 @@
+//! Noise toolkit.
+//!
+//! Rust bindings follow the original API pretty closely.
+
+use bindings::ffi;
+use bindings::AsNative;
+
+use random::Rng;
+
+/// Available noise types.
+#[repr(C)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum NoiseType {
+ Default = ffi::TCOD_NOISE_DEFAULT as isize,
+ Perlin = ffi::TCOD_NOISE_PERLIN as isize,
+ Simplex = ffi::TCOD_NOISE_SIMPLEX as isize,
+ Wavelet = ffi::TCOD_NOISE_WAVELET as isize,
+}
+
+/// Noise object encapsulates a noise generator.
+#[derive(Debug)]
+pub struct Noise {
+ noise: ffi::TCOD_noise_t,
+ dimensions: u32
+}
+
+/// Default hurst value.
+pub const DEFAULT_HURST: f32 = 0.5;
+
+/// Default lacunarity value.
+pub const DEFAULT_LACUNARITY: f32 = 2.0;
+
+/// Maximum number of octaves for turbulence and fbm noise functions.
+pub const MAX_OCTAVES: u32 = 128;
+
+impl Noise {
+ /// Return an instance of [NoiseInitializer](./struct.NoiseInitializer.html)
+ /// which is used to customize the creation of Noise object.
+ pub fn init_with_dimensions(dimensions: u32) -> NoiseInitializer {
+ NoiseInitializer::new_init_with_dimensions(dimensions)
+ }
+
+ pub fn set_type(&self, noise_type: NoiseType) {
+ unsafe {
+ ffi::TCOD_noise_set_type(self.noise, noise_type as u32)
+ }
+ }
+
+ pub fn get>(&self, mut coords: T) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ unsafe {
+ ffi::TCOD_noise_get(self.noise, coords.as_mut().as_mut_ptr())
+ }
+ }
+
+ pub fn get_ex>(&self, mut coords: T, noise_type: NoiseType) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ unsafe {
+ ffi::TCOD_noise_get_ex(self.noise,
+ coords.as_mut().as_mut_ptr(),
+ noise_type as u32)
+ }
+ }
+
+ pub fn get_fbm>(&self, mut coords: T, octaves: u32) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ assert!(octaves > 0);
+ assert!(octaves < MAX_OCTAVES);
+ unsafe {
+ ffi::TCOD_noise_get_fbm(self.noise, coords.as_mut().as_mut_ptr(), octaves as f32)
+ }
+ }
+
+ pub fn get_fbm_ex>(&self, mut coords: T, octaves: u32, noise_type: NoiseType) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ assert!(octaves > 0);
+ assert!(octaves < MAX_OCTAVES);
+ unsafe {
+ ffi::TCOD_noise_get_fbm_ex(self.noise,
+ coords.as_mut().as_mut_ptr(),
+ octaves as f32,
+ noise_type as u32)
+ }
+ }
+
+ pub fn get_turbulence>(&self, mut coords: T, octaves: u32) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ assert!(octaves > 0);
+ assert!(octaves < MAX_OCTAVES);
+ unsafe {
+ ffi::TCOD_noise_get_turbulence(self.noise,
+ coords.as_mut().as_mut_ptr(),
+ octaves as f32)
+ }
+ }
+
+ pub fn get_turbulence_ex>(&self, mut coords: T, octaves: u32, noise_type: NoiseType) -> f32 {
+ assert!(self.dimensions as usize == coords.as_mut().len());
+ assert!(octaves > 0);
+ assert!(octaves < MAX_OCTAVES);
+ unsafe {
+ ffi::TCOD_noise_get_turbulence_ex(self.noise,
+ coords.as_mut().as_mut_ptr(),
+ octaves as f32,
+ noise_type as u32)
+ }
+ }
+}
+
+impl Drop for Noise {
+ fn drop(&mut self) {
+ unsafe { ffi::TCOD_noise_delete(self.noise) }
+ }
+}
+
+/// An initializer is used to customize creation of a `Noise` object.
+pub struct NoiseInitializer {
+ dimensions: u32,
+ hurst: f32,
+ lacunarity: f32,
+ noise_type: NoiseType,
+ random: Rng
+}
+
+impl NoiseInitializer {
+ fn new_init_with_dimensions(dimensions: u32) -> Self {
+ assert!(dimensions > 0 && dimensions <= 4);
+ NoiseInitializer {
+ dimensions: dimensions,
+ hurst: DEFAULT_HURST,
+ lacunarity: DEFAULT_LACUNARITY,
+ noise_type: NoiseType::Default,
+ random: Rng::get_instance()
+ }
+ }
+
+ /// Sets the hurst value of the noise generator.
+ pub fn hurst(&mut self, hurst: f32) -> &mut Self {
+ self.hurst = hurst;
+ self
+ }
+
+ /// Sets the lacunarity value of the noise generator.
+ pub fn lacunarity(&mut self, lacunarity: f32) -> &mut Self {
+ self.lacunarity = lacunarity;
+ self
+ }
+
+ /// Sets the noise type the generator produces.
+ pub fn noise_type(&mut self, noise_type: NoiseType) -> &mut Self {
+ self.noise_type = noise_type;
+ self
+ }
+
+ /// Sets a custom random number generator. Use
+ /// [tcod::random::Rng](../random/struct.Rng.html) instance.
+ pub fn random(&mut self, random: Rng) -> &mut Self {
+ self.random = random;
+ self
+ }
+
+ /// Finalizes creation and returns a new `Noise` object.
+ pub fn init(&self) -> Noise {
+ unsafe {
+ let noise = Noise {
+ noise: ffi::TCOD_noise_new(self.dimensions as i32, self.hurst,
+ self.lacunarity, *self.random.as_native()),
+ dimensions: self.dimensions,
+ };
+ ffi::TCOD_noise_set_type(noise.noise, self.noise_type as u32);
+ noise
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::Noise;
+ use super::NoiseType;
+
+ #[test]
+ fn get() {
+ let noise1d = Noise::init_with_dimensions(1).init();
+ let noise2d = Noise::init_with_dimensions(2).init();
+ let noise3d = Noise::init_with_dimensions(3).init();
+
+ let val1 = noise1d.get([1.0]);
+ let val1a = noise1d.get([1.0]);
+ assert!(val1 >= -1.0 && val1 <= 1.0);
+ assert_eq!(val1, val1a);
+
+ let val2 = noise2d.get([1.0, 2.0]);
+ let val2a = noise2d.get([1.0, 2.0]);
+ assert!(val2 >= -1.0 && val2 <= 1.0);
+ assert_eq!(val2, val2a);
+
+ let val3 = noise3d.get([1.0, 2.0, 3.0]);
+ let val3a = noise3d.get([1.0, 2.0, 3.0]);
+ assert!(val3 >= -1.0 && val3 <= 1.0);
+ assert_eq!(val3, val3a);
+ }
+
+ #[test]
+ #[should_panic]
+ fn init_with_wrong_dimensions() {
+ Noise::init_with_dimensions(5).init();
+ }
+
+ #[test]
+ #[should_panic]
+ fn init_with_zero_dimensions() {
+ Noise::init_with_dimensions(0).init();
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get([1.0]);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get([1.0, 2.0, 3.0]);
+ }
+
+ #[test]
+ fn get_ex() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+
+ let val1 = noise2d.get_ex([1.0, 2.0], NoiseType::Perlin);
+ let val1a = noise2d.get_ex([1.0, 2.0], NoiseType::Perlin);
+ assert!(val1 >= -1.0 && val1 <= 1.0);
+ assert_eq!(val1, val1a);
+
+ let val2 = noise2d.get_ex([1.0, 2.0], NoiseType::Wavelet);
+ let val2a = noise2d.get_ex([1.0, 2.0], NoiseType::Wavelet);
+ assert!(val2 >= -1.0 && val2 <= 1.0);
+ assert_eq!(val2, val2a);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_ex_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_ex([1.0], NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_ex_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_ex([1.0, 2.0, 3.0], NoiseType::Perlin);
+ }
+
+ #[test]
+ fn get_fbm() {
+ let noise1d = Noise::init_with_dimensions(1).init();
+ let noise2d = Noise::init_with_dimensions(2).init();
+ let noise3d = Noise::init_with_dimensions(3).init();
+
+ let val1 = noise1d.get_fbm([1.0], 32);
+ let val1a = noise1d.get_fbm([1.0], 32);
+ assert!(val1.is_nan() || val1 >= -1.0 && val1 <= 1.0);
+ if !val1.is_nan() {
+ assert_eq!(val1, val1a);
+ }
+
+ let val2 = noise2d.get_fbm([1.0, 2.0], 32);
+ let val2a = noise2d.get_fbm([1.0, 2.0], 32);
+ assert!(val2.is_nan() || val2 >= -1.0 && val2 <= 1.0);
+ if !val2.is_nan() {
+ assert_eq!(val2, val2a);
+ }
+
+ let val3 = noise3d.get_fbm([1.0, 2.0, 3.0], 32);
+ let val3a = noise3d.get_fbm([1.0, 2.0, 3.0], 32);
+ assert!(val3.is_nan() || val3 >= -1.0 && val3 <= 1.0);
+ if !val3.is_nan() {
+ assert_eq!(val3, val3a);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm([1.0], 32);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm([1.0, 2.0, 3.0], 32);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_octaves_zero() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm([1.0, 2.0, 3.0], 0);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_octaves_too_big() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm([1.0, 2.0, 3.0], 128);
+ }
+
+ #[test]
+ fn get_fbm_ex() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+
+ let val1 = noise2d.get_fbm_ex([1.0, 2.0], 32, NoiseType::Perlin);
+ let val1a = noise2d.get_fbm_ex([1.0, 2.0], 32, NoiseType::Perlin);
+ assert!(val1.is_nan() || val1 >= -1.0 && val1 <= 1.0);
+ if !val1.is_nan() {
+ assert_eq!(val1, val1a);
+ }
+
+ let val2 = noise2d.get_fbm_ex([1.0, 2.0], 64, NoiseType::Wavelet);
+ let val2a = noise2d.get_fbm_ex([1.0, 2.0], 64, NoiseType::Wavelet);
+ assert!(val2.is_nan() || val2 >= -1.0 && val2 <= 1.0);
+ if !val2.is_nan() {
+ assert_eq!(val2, val2a);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_ex_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm_ex([1.0], 32, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_ex_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm_ex([1.0, 2.0, 3.0], 32, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_ex_octaves_zero() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm_ex([1.0, 2.0, 3.0], 0, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_fbm_ex_octaves_too_big() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_fbm_ex([1.0, 2.0, 3.0], 128, NoiseType::Perlin);
+ }
+
+ #[test]
+ fn get_turbulence() {
+ let noise1d = Noise::init_with_dimensions(1).init();
+ let noise2d = Noise::init_with_dimensions(2).init();
+ let noise3d = Noise::init_with_dimensions(3).init();
+
+ let val1 = noise1d.get_turbulence([1.0], 32);
+ let val1a = noise1d.get_turbulence([1.0], 32);
+ assert!(val1.is_nan() || val1 >= -1.0 && val1 <= 1.0);
+ if !val1.is_nan() {
+ assert_eq!(val1, val1a);
+ }
+
+ let val2 = noise2d.get_turbulence([1.0, 2.0], 32);
+ let val2a = noise2d.get_turbulence([1.0, 2.0], 32);
+ assert!(val2.is_nan() || val2 >= -1.0 && val2 <= 1.0);
+ if !val2.is_nan() {
+ assert_eq!(val2, val2a);
+ }
+
+ let val3 = noise3d.get_turbulence([1.0, 2.0, 3.0], 32);
+ let val3a = noise3d.get_turbulence([1.0, 2.0, 3.0], 32);
+ assert!(val3.is_nan() || val3 >= -1.0 && val3 <= 1.0);
+ if !val3.is_nan() {
+ assert_eq!(val3, val3a);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence([1.0], 32);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence([1.0, 2.0, 3.0], 32);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_octaves_zero() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence([1.0, 2.0, 3.0], 0);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_octaves_too_big() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence([1.0, 2.0, 3.0], 128);
+ }
+
+ #[test]
+ fn get_turbulence_ex() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+
+ let val1 = noise2d.get_turbulence_ex([1.0, 2.0], 32, NoiseType::Perlin);
+ let val1a = noise2d.get_turbulence_ex([1.0, 2.0], 32, NoiseType::Perlin);
+ assert!(val1.is_nan() || val1 >= -1.0 && val1 <= 1.0);
+ if !val1.is_nan() {
+ assert_eq!(val1, val1a);
+ }
+
+ let val2 = noise2d.get_turbulence_ex([1.0, 2.0], 64, NoiseType::Wavelet);
+ let val2a = noise2d.get_turbulence_ex([1.0, 2.0], 64, NoiseType::Wavelet);
+ assert!(val2.is_nan() || val2 >= -1.0 && val2 <= 1.0);
+ if !val2.is_nan() {
+ assert_eq!(val2, val2a);
+ }
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_ex_not_enough_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence_ex([1.0], 32, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_ex_too_many_args() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence_ex([1.0, 2.0, 3.0], 32, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_ex_octaves_zero() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence_ex([1.0, 2.0, 3.0], 0, NoiseType::Perlin);
+ }
+
+ #[test]
+ #[should_panic]
+ fn get_turbulence_ex_octaves_too_big() {
+ let noise2d = Noise::init_with_dimensions(2).init();
+ noise2d.get_turbulence_ex([1.0, 2.0, 3.0], 128, NoiseType::Perlin);
+ }
+}
diff --git a/tcod/src/pathfinding.rs b/tcod/src/pathfinding.rs
new file mode 100644
index 00000000..6cd8a9bf
--- /dev/null
+++ b/tcod/src/pathfinding.rs
@@ -0,0 +1,387 @@
+use bindings::ffi;
+use bindings::{AsNative, c_bool, c_float, c_int, c_void};
+use map::Map;
+
+enum PathInnerData<'a> {
+ Map(Map),
+ Callback(Box f32+'a>),
+}
+
+pub struct AStar<'a>{
+ tcod_path: ffi::TCOD_path_t,
+ #[allow(dead_code)]
+ inner: PathInnerData<'a>,
+ width: i32,
+ height: i32,
+}
+
+impl<'a> AsNative for AStar<'a> {
+ unsafe fn as_native(&self) -> &ffi::TCOD_path_t {
+ &self.tcod_path
+ }
+}
+
+impl<'a> Drop for AStar<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::TCOD_path_delete(self.tcod_path);
+ }
+ }
+}
+
+extern "C" fn c_path_callback f32>(xf: c_int, yf: c_int,
+ xt: c_int, yt: c_int,
+ user_data: *mut c_void) -> c_float {
+ let callback_ptr = user_data as *mut T;
+ let cb: &mut T = unsafe {
+ &mut *callback_ptr
+ };
+ cb((xf, yf), (xt, yt))
+}
+
+impl<'a> AStar<'a> {
+ pub fn new_from_callback f32>(
+ width: i32, height: i32, path_callback: T,
+ diagonal_cost: f32) -> AStar<'a> {
+ let callback = Box::new(path_callback);
+ let user_data = &*callback as *const T as *mut c_void;
+ unsafe {
+ let tcod_path = ffi::TCOD_path_new_using_function(width, height,
+ Some(c_path_callback::),
+ user_data,
+ diagonal_cost);
+ AStar {
+ tcod_path: tcod_path,
+ // We need to keep user_closure around, otherwise it
+ // would get deallocated at the end of this function.
+ inner: PathInnerData::Callback(callback),
+ width: width,
+ height: height,
+ }
+ }
+ }
+
+ pub fn new_from_map(map: Map, diagonal_cost: f32) -> AStar<'static> {
+ let tcod_path = unsafe {
+ ffi::TCOD_path_new_using_map(*map.as_native(), diagonal_cost)
+ };
+ let (w, h) = map.size();
+ AStar {
+ tcod_path: tcod_path,
+ inner: PathInnerData::Map(map),
+ width: w,
+ height: h,
+ }
+ }
+
+ pub fn find(&mut self,
+ from: (i32, i32),
+ to: (i32, i32)) -> bool {
+ let (from_x, from_y) = from;
+ let (to_x, to_y) = to;
+ assert!(from_x >= 0 && from_y >= 0 && to_x >= 0 && to_y >= 0);
+ assert!(from_x < self.width && from_y < self.height && to_x < self.width && to_y < self.height);
+ unsafe {
+ ffi::TCOD_path_compute(self.tcod_path,
+ from_x, from_y,
+ to_x, to_y) != 0
+ }
+ }
+
+ pub fn iter(&'a self) -> AStarPathIter<'a> {
+ AStarPathIter { current: -1, path: self }
+ }
+
+ pub fn walk(&mut self) -> AStarIterator {
+ AStarIterator{tcod_path: self.tcod_path, recalculate: false}
+ }
+
+ pub fn walk_recalculate(&mut self) -> AStarIterator {
+ AStarIterator{tcod_path: self.tcod_path, recalculate: true}
+ }
+
+ pub fn walk_one_step(&mut self, recalculate_when_needed: bool) -> Option<(i32, i32)> {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ match ffi::TCOD_path_walk(self.tcod_path, &mut x, &mut y,
+ recalculate_when_needed as c_bool) != 0 {
+ true => Some((x, y)),
+ false => None,
+ }
+ }
+ }
+
+ pub fn reverse(&mut self) {
+ unsafe {
+ ffi::TCOD_path_reverse(self.tcod_path)
+ }
+ }
+
+ pub fn origin(&self) -> (isize, isize) {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_path_get_origin(self.tcod_path, &mut x, &mut y);
+ (x as isize, y as isize)
+ }
+ }
+
+ pub fn destination(&self) -> (isize, isize) {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_path_get_destination(self.tcod_path, &mut x, &mut y);
+ (x as isize, y as isize)
+ }
+ }
+
+ pub fn get(&self, index: i32) -> Option<(i32, i32)> {
+ if index < 0 || index >= self.len() {
+ return None;
+ }
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_path_get(self.tcod_path, index, &mut x, &mut y);
+ (Some((x, y)))
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ unsafe {
+ ffi::TCOD_path_is_empty(self.tcod_path) != 0
+ }
+ }
+
+ pub fn len(&self) -> i32 {
+ unsafe {
+ ffi::TCOD_path_size(self.tcod_path)
+ }
+ }
+}
+
+pub struct Dijkstra<'a> {
+ tcod_path: ffi::TCOD_dijkstra_t,
+ #[allow(dead_code)]
+ inner: PathInnerData<'a>,
+ width: i32,
+ height: i32,
+}
+
+impl<'a> AsNative for Dijkstra<'a> {
+ unsafe fn as_native(&self) -> &ffi::TCOD_dijkstra_t {
+ &self.tcod_path
+ }
+}
+
+impl<'a> Drop for Dijkstra<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ ffi::TCOD_dijkstra_delete(self.tcod_path);
+ }
+ }
+}
+
+impl<'a> Dijkstra<'a> {
+ pub fn new_from_callback f32>(
+ width: i32, height: i32,
+ path_callback: T,
+ diagonal_cost: f32) -> Dijkstra<'a> {
+ let callback = Box::new(path_callback);
+ let user_data = &*callback as *const T as *mut c_void;
+ unsafe {
+ let tcod_path = ffi::TCOD_dijkstra_new_using_function(width,
+ height,
+ Some(c_path_callback::),
+ user_data,
+ diagonal_cost);
+ Dijkstra {
+ tcod_path: tcod_path,
+ inner: PathInnerData::Callback(callback),
+ width: width,
+ height: height,
+ }
+ }
+ }
+
+ pub fn new_from_map(map: Map, diagonal_cost: f32) -> Dijkstra<'static> {
+ let tcod_path = unsafe {
+ ffi::TCOD_dijkstra_new(*map.as_native(), diagonal_cost)
+ };
+ let (w, h) = map.size();
+ Dijkstra {
+ tcod_path: tcod_path,
+ inner: PathInnerData::Map(map),
+ width: w,
+ height: h,
+ }
+ }
+
+ pub fn compute_grid(&mut self, root: (i32, i32)) {
+ let (x, y) = root;
+ assert!(x >= 0 && y >= 0 && x < self.width && y < self.height);
+ unsafe {
+ ffi::TCOD_dijkstra_compute(self.tcod_path, x, y);
+ }
+ }
+
+ pub fn find(&mut self, destination: (i32, i32)) -> bool {
+ let (x, y) = destination;
+ if x >= 0 && y >= 0 && x < self.width && y < self.height {
+ unsafe {
+ ffi::TCOD_dijkstra_path_set(self.tcod_path, x, y) != 0
+ }
+ } else {
+ false
+ }
+ }
+
+ pub fn iter(&'a self) -> DijkstraPathIter<'a> {
+ DijkstraPathIter { current: -1, path: self }
+ }
+
+ pub fn walk(&mut self) -> DijkstraIterator {
+ DijkstraIterator{tcod_path: self.tcod_path}
+ }
+
+ pub fn walk_one_step(&mut self) -> Option<(i32, i32)> {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ match ffi::TCOD_dijkstra_path_walk(self.tcod_path, &mut x, &mut y) != 0 {
+ true => Some((x, y)),
+ false => None,
+ }
+ }
+ }
+
+
+ pub fn distance_from_root(&self, point: (i32, i32)) -> Option {
+ let (x, y) = point;
+ let result = unsafe {
+ ffi::TCOD_dijkstra_get_distance(self.tcod_path, x, y)
+ };
+ if result == -1.0 {
+ None
+ } else {
+ Some(result)
+ }
+ }
+
+ pub fn reverse(&mut self) {
+ unsafe {
+ ffi::TCOD_dijkstra_reverse(self.tcod_path);
+ }
+ }
+
+ pub fn get(&self, index: i32) -> Option<(i32, i32)> {
+ if index < 0 || index >= self.len() {
+ return None;
+ }
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_dijkstra_get(self.tcod_path, index, &mut x, &mut y);
+ Some((x, y))
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ unsafe {
+ ffi::TCOD_dijkstra_is_empty(self.tcod_path) != 0
+ }
+ }
+
+ pub fn len(&self) -> i32 {
+ unsafe {
+ ffi::TCOD_dijkstra_size(self.tcod_path)
+ }
+ }
+}
+
+pub struct AStarIterator {
+ tcod_path: ffi::TCOD_path_t,
+ recalculate: bool,
+}
+
+impl Iterator for AStarIterator {
+ type Item = (i32, i32);
+
+ fn next(&mut self) -> Option<(i32, i32)> {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ match ffi::TCOD_path_walk(self.tcod_path, &mut x, &mut y,
+ self.recalculate as c_bool) != 0 {
+ true => Some((x, y)),
+ false => None,
+ }
+ }
+ }
+}
+
+pub struct DijkstraIterator {
+ tcod_path: ffi::TCOD_path_t,
+}
+
+impl Iterator for DijkstraIterator {
+ type Item = (i32, i32);
+
+ fn next(&mut self) -> Option<(i32, i32)> {
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ match ffi::TCOD_dijkstra_path_walk(self.tcod_path, &mut x, &mut y) != 0 {
+ true => Some((x, y)),
+ false => None,
+ }
+ }
+ }
+}
+
+pub struct AStarPathIter<'a> {
+ current: i32,
+ path: &'a AStar<'a>,
+}
+
+impl<'a> Iterator for AStarPathIter<'a> {
+ type Item = (i32, i32);
+
+ fn next(&mut self) -> Option {
+ if self.current == self.path.len() - 1 {
+ None
+ } else {
+ self.current += 1;
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_path_get(*self.path.as_native(), self.current, &mut x, &mut y);
+ Some((x, y))
+ }
+ }
+ }
+}
+
+pub struct DijkstraPathIter<'a> {
+ current: i32,
+ path: &'a Dijkstra<'a>,
+}
+
+impl<'a> Iterator for DijkstraPathIter<'a> {
+ type Item = (i32, i32);
+
+ fn next(&mut self) -> Option {
+ if self.current == self.path.len() - 1 {
+ None
+ } else {
+ self.current += 1;
+ unsafe {
+ let mut x: c_int = 0;
+ let mut y: c_int = 0;
+ ffi::TCOD_dijkstra_get(*self.path.as_native(), self.current, &mut x, &mut y);
+ Some((x, y))
+ }
+ }
+ }
+}
diff --git a/tcod/src/random.rs b/tcod/src/random.rs
new file mode 100644
index 00000000..3212756e
--- /dev/null
+++ b/tcod/src/random.rs
@@ -0,0 +1,117 @@
+use bindings::ffi;
+use bindings::AsNative;
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum Distribution {
+ Linear = ffi::TCOD_DISTRIBUTION_LINEAR as isize,
+ Gaussian = ffi::TCOD_DISTRIBUTION_GAUSSIAN as isize,
+ GaussianRange = ffi::TCOD_DISTRIBUTION_GAUSSIAN_RANGE as isize,
+ GaussianInverse = ffi::TCOD_DISTRIBUTION_GAUSSIAN_INVERSE as isize,
+ GaussianRangeInverse = ffi::TCOD_DISTRIBUTION_GAUSSIAN_RANGE_INVERSE as isize,
+
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum Algo {
+ MT = ffi::TCOD_RNG_MT as isize,
+ CMWC = ffi::TCOD_RNG_CMWC as isize
+}
+
+pub struct Rng {
+ tcod_random: ffi::TCOD_random_t,
+ default: bool
+}
+
+impl Rng {
+ pub fn get_instance() -> Rng {
+ unsafe {
+ Rng { tcod_random: ffi::TCOD_random_get_instance(), default: true }
+ }
+ }
+
+ pub fn new(algo: Algo) -> Rng {
+ unsafe {
+ Rng { tcod_random: ffi::TCOD_random_new(algo as u32), default: false }
+ }
+ }
+
+ pub fn new_with_seed(algo: Algo, seed: u32) -> Rng {
+ unsafe {
+ Rng { tcod_random: ffi::TCOD_random_new_from_seed(algo as u32, seed), default: false }
+ }
+ }
+
+ pub fn save(&self) -> Rng {
+ unsafe {
+ Rng { tcod_random: ffi::TCOD_random_save(self.tcod_random), default: false }
+ }
+ }
+
+ pub fn restore(&mut self, backup: &Rng) {
+ unsafe {
+ ffi::TCOD_random_restore(self.tcod_random, backup.tcod_random);
+ }
+ }
+
+ pub fn set_distribution(&self, distribution: Distribution) {
+ unsafe {
+ ffi::TCOD_random_set_distribution(self.tcod_random, distribution as u32);
+ }
+ }
+
+ pub fn get_int(&self, min: i32, max: i32) -> i32 {
+ unsafe {
+ ffi::TCOD_random_get_int(self.tcod_random, min, max)
+ }
+ }
+
+ pub fn get_int_mean(&self, min: i32, max: i32, mean: i32) -> i32 {
+ unsafe {
+ ffi::TCOD_random_get_int_mean(self.tcod_random, min, max, mean)
+ }
+ }
+
+ pub fn get_float(&self, min: f32, max: f32) -> f32 {
+ unsafe {
+ ffi::TCOD_random_get_float(self.tcod_random, min, max)
+ }
+ }
+
+ pub fn get_float_mean(&self, min: f32, max: f32, mean: f32) -> f32 {
+ unsafe {
+ ffi::TCOD_random_get_float_mean(self.tcod_random, min, max, mean)
+ }
+ }
+
+ pub fn get_double(&self, min: f64, max: f64) -> f64 {
+ unsafe {
+ ffi::TCOD_random_get_double(self.tcod_random, min, max)
+ }
+ }
+
+ pub fn get_double_mean(&self, min: f64, max: f64, mean: f64) -> f64 {
+ unsafe {
+ ffi::TCOD_random_get_double_mean(self.tcod_random, min, max, mean)
+ }
+ }
+}
+
+impl AsNative for Rng {
+ unsafe fn as_native(&self) -> &ffi::TCOD_random_t {
+ &self.tcod_random
+ }
+}
+
+impl Drop for Rng {
+ fn drop(&mut self) {
+ if !self.default {
+ unsafe {
+ ffi::TCOD_random_delete(self.tcod_random);
+ }
+ }
+ }
+}
+
+
diff --git a/tcod/src/rustc_serialize_impls.rs b/tcod/src/rustc_serialize_impls.rs
new file mode 100644
index 00000000..814a9ff5
--- /dev/null
+++ b/tcod/src/rustc_serialize_impls.rs
@@ -0,0 +1,47 @@
+#![cfg(feature = "rustc-serialize")]
+
+use super::Color;
+use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
+
+impl Encodable for Color {
+ fn encode(&self, s: &mut S) -> Result<(), S::Error> {
+ s.emit_struct("Color", 3, |s| {
+ try!{ s.emit_struct_field("r", 0, |s| self.r.encode(s)) };
+ try!{ s.emit_struct_field("g", 1, |s| self.g.encode(s)) };
+ try!{ s.emit_struct_field("b", 2, |s| self.b.encode(s)) };
+ Ok(())
+ })
+ }
+}
+
+#[cfg(feature = "rustc-serialize")]
+impl Decodable for Color {
+ fn decode(d: &mut D) -> Result {
+ d.read_struct("Color", 3, |d| {
+ let r = try!(d.read_struct_field("r", 0, |d| d.read_u8()));
+ let g = try!(d.read_struct_field("g", 1, |d| d.read_u8()));
+ let b = try!(d.read_struct_field("b", 2, |d| d.read_u8()));
+ Ok(Color{r: r, g: g, b: b})
+ })
+ }
+}
+
+
+
+#[cfg(test)]
+mod test {
+ use ::Color;
+ use ::rustc_serialize::json;
+
+ #[test]
+ fn color_encode() {
+ let encoded = json::encode(&Color{r: 1, g: 2, b: 3}).unwrap();
+ assert_eq!("{\"r\":1,\"g\":2,\"b\":3}", encoded);
+ }
+
+ #[test]
+ fn color_decode() {
+ let decoded: Color = json::decode("{\"r\":1,\"g\":2,\"b\":3}").unwrap();
+ assert_eq!(Color{r: 1, g: 2, b: 3}, decoded);
+ }
+}
diff --git a/tcod/src/serde_impls.rs b/tcod/src/serde_impls.rs
new file mode 100644
index 00000000..28ed9160
--- /dev/null
+++ b/tcod/src/serde_impls.rs
@@ -0,0 +1,138 @@
+#![cfg(feature = "serde")]
+
+use super::Color;
+
+use serde::ser::{self, Serialize, Serializer};
+use serde::de::{self, Deserialize, Deserializer};
+
+impl Serialize for Color {
+ fn serialize(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
+ serializer.visit_struct("Color", ColorMapVisitor {
+ value: self,
+ state: 0,
+ })
+ }
+}
+
+struct ColorMapVisitor<'a> {
+ value: &'a Color,
+ state: u8,
+}
+
+impl<'a> ser::MapVisitor for ColorMapVisitor<'a> {
+ fn visit(&mut self, serializer: &mut S) -> Result