use std::collections::HashMap; use sfml::window::mouse::Button; use sfml::system::{ Vector2f, Vector2i }; use sfml::window::{ ContextSettings, Event, Style, }; use sfml::graphics::{ RenderWindow, RenderTarget, View, }; use sfml::graphics::{ Texture, Image, Color, }; use sfml::graphics::{ RectangleShape, Transformable, Rect, }; #[derive(RustEmbed)] #[folder = "res/maps"] pub struct MapAssets; pub struct Graphics { // rendering elements window: RenderWindow, scenes: Vec>, current_scene: Box, // status running: bool, } 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::DEFAULT, &context_settings ); window.set_framerate_limit(default_framerate); window.set_vertical_sync_enabled(true); let map_scene = Box::new(MapScene::new(&window)); return Graphics { window: window, scenes: Vec::new(), current_scene: map_scene, running: true, }; } pub fn is_running(self: &mut Self) -> bool { return self.running; } pub fn update(self: &mut Self) { while let Some(event) = self.window.poll_event() { match event { Event::Closed => { self.window.close(); self.running = false; } _ => {} } self.current_scene.event(&mut self.window, event) } } 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); self.current_scene.render(&mut self.window, state); self.window.display(); } } trait Scene { fn render(self: &mut Self, window: &mut RenderWindow, state: &crate::game::State); fn event(self: &mut Self, window: &mut RenderWindow, event: Event); } struct MapScene { map_view: View, // pan panning: bool, pan_start: Vector2f, // loaded resources tilesets: HashMap, textures: HashMap, } impl MapScene { fn new(window: &RenderWindow) -> MapScene { return MapScene { map_view: window.default_view().to_owned(), panning: false, pan_start: Vector2f::new(0.0, 0.0), tilesets: HashMap::new(), textures: HashMap::new() }; } } impl MapScene { fn load_tilset(self: &mut Self, tmx_tileset: &tiled::Tileset) { // load tileset if not loaded yet if self.tilesets.contains_key(&tmx_tileset.first_gid) { return; } // 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); } } impl Scene for MapScene { fn render(self: &mut Self, window: &mut RenderWindow, state: &crate::game::State) { 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 => { // TODO: missing_tile texture panic!("Failed to get tileset with get_tileset_by_gid"); } }; // TODO: load in a separate thread? self.load_tilset(tmx_tileset); // draw tiles // 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 }); window.draw(&tile_rect); } } } } fn event(self: &mut Self, window: &mut RenderWindow, event: Event) { match event { Event::MouseWheelScrolled { wheel: _, delta, x: _, y: _ } => { // delta is probalby a value between -1 and 1 let zoom = 1.0 + delta * 0.1; self.map_view.zoom(zoom); window.set_view(&self.map_view); }, Event::MouseButtonPressed { button, x, y} => { match button { Button::Right => {}, Button::Left => { // set pan start position self.panning = true; self.pan_start = window.map_pixel_to_coords_current_view(&Vector2i::new(x, y)); } _ => {}, } }, Event::MouseButtonReleased { button, x: _, y: _ } => { match button { Button::Right => {} Button::Left => { self.panning = false; }, _ => {}, } }, Event::MouseMoved { x, y } => { if self.panning { // get delta of vectors let delta = self.pan_start - window.map_pixel_to_coords_current_view(&Vector2i::new(x, y)); // move view by moving the center by delta self.map_view.move_(delta); window.set_view(&self.map_view); } } _ => {}, } } }