1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
pub use super::{SuperCover, Line, Vector, TileNet, TileSet};
pub use interleave::{IterList, MultiIter};
/// A vertex iterator.
///
/// Used internally by the collision engine. It combines static
/// points with an offset. Every iteration returns the point + offset.
pub struct Points<'a> {
index: usize,
offset: Vector,
points: &'a [(f32, f32)],
}
impl<'a> Points<'a> {
/// Create a new points iterator
///
/// The points iterator creates an iterator over all points by adding the vector offset vector.
///
/// ```
/// use tile_net::*;
/// fn main() {
/// let initial = vec![(0.0, 0.0), (1.0, 0.0)];
/// let mut points = Points::new(Vector(3.0, 4.0), &initial);
/// assert_eq![points.next().unwrap(), (3.0, 4.0)];
/// assert_eq![points.next().unwrap(), (4.0, 4.0)];
/// }
/// ```
///
pub fn new(offset: Vector, points: &'a [(f32, f32)]) -> Points {
Points {
index: 0,
offset: offset,
points: points,
}
}
}
impl<'a> Iterator for Points<'a> {
type Item = (f32, f32);
fn next(&mut self) -> Option<Self::Item> {
let ret = self.points
.get(self.index)
.cloned()
.map(|x| Vector::from_tuple(x) + self.offset)
.map(|x| (x.0, x.1));
self.index += 1;
ret
}
}
/// Trait for dynamic objects so they can easily check collisions with the `TileMap`
pub trait Collable<T, S> {
/// Returns the set of points associated with this object. These points are used to
/// draw lines to their respective next points. For a rectangle, the four courners
/// may be points. For a circle, a whole bunch of points may be defined.
fn points(&self) -> Points;
/// Get the previously queued move. Should reasonably return what was given in `enqueue`,
/// but you can do whatever makes sense in your application.
fn queued(&self) -> Vector;
/// Resolve the movement: you get a set of tiles and you decide what to do with them.
/// If you aren't satisfied, you can change the move vector and return false, this means
/// that we'll try again. Another set of tiles may then be given.
/// If you're satisfied, return true and adjust your `Collable`'s position accordingly.
///
/// IMPORTANT: You should add the move from queued_move to your point set. The ray tracer
/// also adds to find the next points. This will prevent you from getting stuck in a wall.
fn resolve<I>(&mut self, set: TileSet<T, I>, state: &mut S) -> bool
where I: Iterator<Item = (i32, i32)>;
/// Called at the beginning of `solve`
///
/// This method is useful when resetting internal variables of state.
/// An example of this is when you have to set a has-jumped variable.
fn presolve(&mut self, _state: &mut S) {}
/// Called at the end of `solve`.
///
/// Used to process the result from the resolve loop.
fn postsolve(&mut self, _collided_once: bool, _resolved: bool, _state: &mut S) {}
/// Convenience function for the resolve loop
///
/// Calls presolve at the beginning and postsolve at the end.
/// Runs the resolve function in a loop of at max 30 iterations.
/// This is to avoid potential deadlock if the resolve function
/// is poorly coded and returns false all the time.
fn solve(&mut self, net: &TileNet<T>, state: &mut S) {
self.presolve(state);
static MAX_ITERATIONS: usize = 30;
let mut collided_once = false;
let mut resolved = false;
for _ in 0..MAX_ITERATIONS {
let tiles = net.collide_set(self.tiles());
if self.resolve(tiles, state) {
resolved = true;
break;
}
collided_once = true;
}
self.postsolve(collided_once, resolved, state);
}
/// Gives us a list of points, sorted by proximity on the line.
///
/// The sortedness of the returned iterator means you can base your decision on the
/// first element(s), as they represent the first collision.
fn tiles(&self) -> MultiIter<(i32, i32)> {
let origin = self.points();
let mut destination = self.points();
destination.offset += self.queued();
let mut multi = interleave!((i32, i32););
for point1 in origin {
let point2 = destination.next().unwrap();
let point1 = Vector::from_tuple(point1);
let point2 = Vector::from_tuple(point2);
let line = Line(point1, point2);
multi.push(Box::new(line.supercover()));
}
multi
}
}