use std::collections::HashMap; use sfml::system::Vector2f; use sfml::window::mouse::Button; use sfml::window::{ ContextSettings, Event, Style, }; use sfml::graphics::{ RenderWindow, RenderTarget, View, }; use sfml::graphics::{ Texture, Image, Color, Rect, }; use sfml::graphics::{ RectangleShape, Transformable, }; #[derive(RustEmbed)] #[folder = "res/maps"] pub struct MapAssets; #[derive(Debug)] pub struct Graphics { // sfml elements window: RenderWindow, view: View, // status running: bool, // loaded resources tilesets: HashMap, textures: HashMap, } impl Graphics { pub fn new() -> Graphics { let default_window_size = (1280, 720); let default_framerate = 80; let context_settings = ContextSettings { antialiasing_level: 0, ..Default::default() }; // create window let mut window = RenderWindow::new( default_window_size, "Subconscious", Style::CLOSE, &context_settings ); window.set_framerate_limit(default_framerate); window.set_vertical_sync_enabled(true); let mut graphics = Graphics { // sfml elements window: window, view: View::new( Vector2f::new( default_window_size.0 as f32 / 2.0, default_window_size.1 as f32 / 2.0 ), Vector2f::new( default_window_size.0 as f32, default_window_size.1 as f32, ), ), // status running: true, // resources tilesets: HashMap::new(), textures: HashMap::new(), }; graphics.window.set_view(&graphics.view); return graphics; } pub fn is_running(self: &mut Self) -> bool { return self.running; } pub fn render(self: &mut Self, state: &crate::game::State) { // background let bgcolor = match state.map.background_colour { Some(color) => Color::rgb(color.red, color.green, color.blue), None => Color::BLACK, }; self.window.clear(&bgcolor); // render map // for each tile for y in 0 .. state.map.height { for x in 0 .. state.map.width { // for each layer for layer in &state.map.layers { let tmx_tile = layer.tiles[y as usize][x as usize]; // blank tile if tmx_tile <= 0 { continue; } let tmx_tileset = match state.map.get_tileset_by_gid(tmx_tile) { Some(tileset) => tileset, None => { panic!("Failed to get tileset with get_tileset_by_gid"); } }; // load tileset if not loaded yet // TODO: load in a separate thread? if !self.tilesets.contains_key(&tmx_tileset.first_gid) { // TODO: replace with iter or error message assert_eq!(tmx_tileset.images.len(), 1); // load tileset image let tmx_image = &tmx_tileset.images[0]; let asset = match MapAssets::get(&tmx_image.source) { Some(asset) => asset, None => { panic!("Failed to get asset: {}", &tmx_image.source); } }; let image = match Image::from_memory(asset.as_ref()) { Some(image) => image, None => { panic!("Failed load image from asset"); } }; // load tiles (textures) let tileset_width = tmx_image.width as u32 / tmx_tileset.tile_width; let tileset_height = tmx_image.height as u32 / tmx_tileset.tile_height; for ty in 0 .. tileset_height { for tx in 0 .. tileset_width { let texture = Texture::from_image_with_rect( &image, &Rect::new( (tx * tmx_tileset.tile_width) as i32, (ty * tmx_tileset.tile_height) as i32, tmx_tileset.tile_width as i32, tmx_tileset.tile_height as i32 ) ).unwrap(); // save tile texture self.textures.insert( tmx_tileset.first_gid + ty * tileset_width + tx, texture ); } } // save tileset image self.tilesets.insert(tmx_tileset.first_gid, image); } // draw! // TODO: check map render order let texture = &self.textures.get(&tmx_tile).unwrap(); let mut tile_rect = RectangleShape::with_texture(texture); tile_rect.set_size(Vector2f { x: tmx_tileset.tile_width as f32, y: tmx_tileset.tile_height as f32 }); tile_rect.set_position(Vector2f { x: (x * tmx_tileset.tile_width) as f32, y: (y * tmx_tileset.tile_height) as f32 }); self.window.draw(&tile_rect); } } } self.window.display(); } pub fn update(self: &mut Self) { while let Some(ev) = self.window.poll_event() { match ev { Event::Closed => { self.window.close(); self.running = false; }, Event::MouseWheelScrolled { wheel: _, delta, x, y } => { }, Event::MouseButtonPressed { button, x: _, y: _ } => { match button { Button::Left => {}, Button::Right => {} _ => {}, } }, Event::MouseButtonReleased { button, x: _, y: _ } => { match button { Button::Left => {} Button::Right => {}, _ => {}, } }, _ => {}, } } } }