tile_net 2.0.5

Continuous tile-based collision detection and resolution.
Documentation
#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]
extern crate sfml;
extern crate tile_net;

use sfml::graphics::{Color, RectangleShape, RenderTarget, RenderWindow, Shape, Transformable,
                     Drawable, RenderStates, View};
use sfml::window::{Key, VideoMode, event, window_style};
use sfml::system::Vector2f;
use tile_net::*;

static SIZE: f32 = 30.0;
static WINDOW: (u32, u32) = (800, 600);

fn main() {

	let mut window = create_window();
	let net = create_tilenet();
	let mut tile = create_block();
	let mut coller = Rects::new();
	let gravity = 0.00981;

	println!["Use WASD to move around"];

	'main: loop {
		if handle_events(&mut window) {
			break 'main;
		}

		let side_speed = 0.4;
		let vert_speed = 0.45;
		if Key::A.is_pressed() {
			coller.enqueue(Vector(-side_speed, 0.0));
		}
		if Key::D.is_pressed() {
			coller.enqueue(Vector(side_speed, 0.0));
		}

		coller.solve(&net, &mut ColState::check_x());

		if Key::W.is_pressed() && coller.jmp {
			coller.set_speed(Vector(0.0, -vert_speed));
			coller.jmp = false;
		}
		if Key::S.is_pressed() {
			coller.enqueue(Vector(0.0, vert_speed * 100000.0));
		}

		coller.enqueue(Vector(0.0, gravity));
		coller.solve(&net, &mut ColState::default());

		window.clear(&Color::new_rgb(255, 255, 255));
		let mut view = View::new_init(&Vector2f::new(0.0, 0.0),
		                              &Vector2f::new(WINDOW.0 as f32, WINDOW.1 as f32))
		                              .unwrap();
		let pos = coller.get_pos();
		view.set_center(&Vector2f::new(pos.0 * SIZE, pos.1 * SIZE));
		window.set_view(&view);

		for i in net.view_center_f32((pos.0, pos.1), (120usize, 60usize)) {
			if let (&1, col, row) = i {
				let col = col as f32;
				let row = row as f32;
				tile.set_position(&Vector2f::new(col * SIZE, row * SIZE));
				window.draw(&tile);
			}
		}
		window.draw(&coller);
		window.display();
	}
}

fn create_window() -> RenderWindow {
	let mut window = RenderWindow::new(VideoMode::new_init(WINDOW.0, WINDOW.1, 42),
	                                   "Custom shape",
	                                   window_style::CLOSE,
	                                   &Default::default())
		.unwrap_or_else(|| {
			panic!("Could not create window");
		});
	window.set_framerate_limit(60);
	window
}

fn create_tilenet() -> tile_net::TileNet<usize> {
	let mut net: TileNet<usize> = tile_net::TileNet::new(10, 10);
	net.set_box(&0, (0, 0), (10, 10));
	net.set_box(&1, (1, 1), (9, 9));
	net.set_box(&0, (2, 2), (8, 8));
	net.set_box(&1, (4, 4), (6, 6));
	net
}

fn create_block<'a>() -> RectangleShape<'a> {
	let mut block = RectangleShape::new().unwrap();
	block.set_size(&Vector2f::new(SIZE, SIZE));
	block.set_fill_color(&Color::new_rgb(0, 0, 0));
	block
}

fn handle_events(window: &mut RenderWindow) -> bool {
	for event in window.events() {
		match event {
			event::Closed => return true,
			event::KeyPressed { code, .. } => {
				if let Key::Escape = code {
					return true;
				}
			}
			_ => {}
		}
	}
	false
}

#[derive(Debug)]
struct Rects {
	pts: Vec<(f32, f32)>,
	pos: Vector,
	mov: Vector,
	jmp: bool,
	checking_x: bool,
	downward: bool,
}

impl Rects {
	fn new() -> Rects {
		Rects {
			pts: vec![(0.0, 0.0), (0.99, 0.0), (0.0, 0.99), (0.99, 0.99)],
			pos: Vector(2.0, 2.0),
			mov: Vector(0.0, 0.0),
			jmp: false,
			checking_x: false,
			downward: false,
		}
	}

	fn set_speed(&mut self, vec: Vector) {
		self.mov = vec;
	}

	fn get_pos(&self) -> Vector {
		self.pos
	}

	fn enqueue(&mut self, vector: Vector) {
		self.mov = self.mov + vector;
	}
}

struct ColState {
	checking_x: Option<f32>,
	downward: bool,
}

impl Default for ColState {
	fn default() -> ColState {
		ColState {
			checking_x: None,
			downward: false,
		}
	}
}

impl ColState {
	fn check_x() -> ColState {
		ColState {
			checking_x: Some(0.0),
			downward: false,
		}
	}
}

impl Collable<usize, ColState> for Rects {
	fn presolve(&mut self, state: &mut ColState) {
		if state.checking_x.is_some() {
			state.checking_x = Some(self.mov.1);
			self.mov = Vector(self.mov.0, 0.0);
		} else {
			state.downward = self.mov.1 > 1e-6;
		}
	}

	fn postsolve(&mut self, collided_once: bool, _resolved: bool, state: &mut ColState) {
		if let Some(dy) = state.checking_x {
			self.mov = Vector(self.mov.0, dy);
		} else {
			if collided_once && state.downward {
				self.jmp = true;
			} else {
				self.jmp = false;
			}
		}
	}

	fn points(&self) -> Points {
		Points::new(self.pos, &self.pts)
	}

	fn queued(&self) -> Vector {
		self.mov
	}

	fn resolve<I>(&mut self, mut set: TileSet<usize, I>, state: &mut ColState) -> bool
		where I: Iterator<Item = (i32, i32)>
	{
		let mut mov = self.mov;
		self.mov = Vector(0.0, 0.0);
		if set.all(|x| *x == 0usize) {
			self.pos = self.pos + mov;
			self.mov = Vector(0.0, mov.1);
			true
		} else if mov.norm2sq() > 1e-6 {
			if state.checking_x.is_some() {
				mov = Vector(mov.0 * 0.59, mov.1);
				self.mov = mov;
			} else {
				mov.scale(0.6);
				self.mov = mov;
			}
			false
		} else {
			true
		}
	}
}

impl Drawable for Rects {
	fn draw<R: RenderTarget>(&self, rt: &mut R, _: &mut RenderStates) {
		let mut block = create_block();
		block.set_position(&Vector2f::new(self.pos.0 * SIZE, self.pos.1 * SIZE));
		rt.draw(&block);
	}
}