gdritter repos chenoska / 8bda3a4
Port over some of the animaltransiro board code Getty Ritter 5 years ago
6 changed file(s) with 321 addition(s) and 17 deletion(s). Collapse all Expand all
1717 # panem-nostrum-quotidianum = { path = "../panem-nostrum-quotidianum/" }
1818 imagefmt = "*"
1919 rodio = "0.6.0"
20 tiled = "*"
1717 None => return Err(
1818 Error::OtherError("Unable to find default sound device".to_owned())),
1919 };
20
21 for fmt in endpoint.supported_formats().unwrap() {
22 fmt.with_max_samples_rate(16000);
23 println!("{:?}", fmt);
24 }
2025
2126 let mut audio_queue = rodio::Sink::new(&endpoint);
2227 audio_queue.set_volume(0.5);
1 use std::ops::{Add, Sub};
2
3 #[derive(PartialEq, Eq, Debug, Clone, Copy)]
4 pub struct Pair<T> {
5 pub x: T,
6 pub y: T,
7 }
8 pub type Coord = Pair<u16>;
9
10 impl Pair<f32> {
11 pub fn to_discrete(&self, size: f32) -> Coord {
12 Pair {
13 x: (self.x / size).floor() as u16,
14 y: (self.y / size).floor() as u16,
15 }
16 }
17 }
18
19 impl<T> Pair<T> {
20 pub fn new(x: T, y: T) -> Pair<T> {
21 Pair { x, y }
22 }
23 }
24
25 impl<T: Add<T, Output=T> + Sub<T, Output=T> + From<u8>> Pair<T> {
26 pub fn zero() -> Pair<T> {
27 Pair {
28 x: T::from(0u8),
29 y: T::from(0u8),
30 }
31 }
32
33 pub fn left(self) -> Pair<T> {
34 Pair { x: self.x - T::from(1u8), y: self.y }
35 }
36
37 pub fn right(self) -> Pair<T> {
38 Pair { x: self.x + T::from(1u8), y: self.y }
39 }
40 }
41
42 impl<O, T: Add<T, Output=O>> Add<Pair<T>> for Pair<T> {
43 type Output = Pair<O>;
44
45 fn add(self, other: Pair<T>) -> Pair<O> {
46 Pair {
47 x: self.x + other.x,
48 y: self.y + other.y,
49 }
50 }
51 }
52
53 impl<O, T: Add<T, Output=O> + Clone> Add<T> for Pair<T> {
54 type Output = Pair<O>;
55
56 fn add(self, r: T) -> Pair<O> {
57 Pair {
58 x: self.x + r.clone(),
59 y: self.y + r,
60 }
61 }
62 }
1 #![allow(dead_code)]
2
3 use std::ops::{Index, IndexMut};
4 use tiled;
5
6 pub mod coord;
7
8 use board::coord::{Coord, Pair};
9
10 const BOARD_WIDTH: u16 = 16;
11 const BOARD_HEIGHT: u16 = 12;
12 const TILE_SIZE: u16 = 24;
13 const HALF_TILE_SIZE: f32 = 24.0;
14
15 #[derive(PartialEq, Eq, Debug)]
16 pub struct Tile {
17 sprite: Coord,
18 }
19
20 impl Tile {
21 fn passable(&self) -> bool {
22 true
23 }
24 }
25
26 #[derive(Debug)]
27 pub struct Entity {
28 loc: Pair<f32>,
29 dir: Pair<f32>,
30 }
31
32 impl Entity {
33 fn blank() -> Entity {
34 Entity {
35 loc: Pair::zero(),
36 dir: Pair::zero(),
37 }
38 }
39
40 fn check_collision(&self, b: &Board, dir: Pair<f32>) -> bool {
41 // so, the deal here is that we are looking at the position of
42 // the top-left corner of a sprite, not at the center. In the
43 // optimal case (if the target is in the top-left corner of a
44 // tile) we only need to check passability for a single tile,
45 // but usually we'll need to check two to four:
46 //
47 // +---+---+
48 // | | |
49 // | @---+|
50 // +--| |+
51 // | | ||
52 // | +---+|
53 // +---+---+
54 //
55 // The point @ in the above diagram is what's called `tgt`
56 // here: the tile it's in is called `tl`, the tile to its
57 // right is `tr`, the tile below is `bl`, and the tile below
58 // and to the right is `br`.
59 let tgt = (self.loc + dir).to_discrete(TILE_SIZE as f32);
60
61 let tl = || b.passable(tgt);
62 let tr = || b.passable(tgt + Pair::new(1, 0));
63 let bl = || b.passable(tgt + Pair::new(0, 1));
64 let br = || b.passable(tgt + Pair::new(1, 1));
65
66 // If we're exactly boundary-aligned, we can check fewer of
67 // these, but otherwise, we can only pass into the place in
68 // question iff all of them are free. (They're all closures so
69 // we can avoid doing unnecessary lookups if we don't have
70 // to.)
71 match tgt {
72 Pair { x: 0, y: 0 } => tl(),
73 Pair { x: 0, y: _ } => tl() && bl(),
74 Pair { x: _, y: 0 } => tl() && tr(),
75 Pair { x: _, y: _ } => tl() && tr() && bl() && br(),
76 }
77 }
78
79 fn get_focus<'a>(&self, b: &'a Board) -> &'a Vec<Entity> {
80 b.get_entity(self.game_coords())
81 }
82
83 fn mv(&self, b: &Board) -> Delta {
84 if self.dir == Pair::zero() {
85 return Delta::zero();
86 }
87 let mut delta = Delta { dx: 0.0, dy: 0.0 };
88
89 // figure out which directions we _can_ travel based on
90 // collision data: if we're trying to move NW, but W is
91 // blocked, then we should still be able to move N.
92 if self.check_collision(b, self.dir) {
93 delta.dx = self.dir.x;
94 delta.dy = self.dir.y;
95 } else if self.check_collision(b, Pair { x: 0.0, ..self.dir }) {
96 delta.dy = self.dir.y;
97 } else if self.check_collision(b, Pair { y: 0.0, ..self.dir }) {
98 delta.dx = self.dir.x;
99 } else {
100 // zeroes are fine
101 };
102
103 if self.dir.x.eq(&0.0) && !self.dir.y.eq(&0.0) {
104 let xm = self.loc.x % HALF_TILE_SIZE;
105 if 0.0 < xm && xm < HALF_TILE_SIZE && self.check_collision(b, Pair { x: -1.0, y: 0.0 }) {
106 delta.dx = delta.dx - 1.0;
107 } else if xm >= HALF_TILE_SIZE && self.check_collision(b, Pair { x: 1.0, y: 0.0 }) {
108 delta.dx = delta.dx + 1.0;
109 }
110 } else if !self.dir.x.eq(&0.0) && self.dir.y.eq(&0.0) {
111 let ym = self.loc.y % HALF_TILE_SIZE;
112 if 0.0 < ym && ym < HALF_TILE_SIZE && self.check_collision(b, Pair { x: 0.0, y: -1.0 }) {
113 delta.dy = delta.dy - 1.0;
114 } else if ym >= HALF_TILE_SIZE && self.check_collision(b, Pair { x: 0.0, y: -1.0 }) {
115 delta.dy = delta.dy + 1.0;
116 }
117 }
118
119 delta
120 }
121
122 fn game_coords(&self) -> Coord {
123 self.loc.to_discrete(TILE_SIZE as f32)
124 }
125 }
126
127 #[derive(Debug)]
128 struct Delta {
129 dx: f32,
130 dy: f32,
131 }
132
133 impl Delta {
134 fn zero() -> Delta {
135 Delta { dx: 0.0, dy: 0.0 }
136 }
137 }
138
139 #[derive(Debug)]
140 pub struct Spritesheet {
141 image: ::imagefmt::Image<u8>,
142 }
143
144 #[derive(Debug)]
145 pub struct Grid<T> {
146 width: u16,
147 height: u16,
148 elems: Vec<T>,
149 }
150
151 impl<T> Grid<T> {
152 pub fn new_with<F: Fn(u16, u16) -> T>(width: u16, height: u16, elem: F) -> Grid<T> {
153 let mut elems = Vec::with_capacity((width * height) as usize);
154 for y in 0..height {
155 for x in 0..width {
156 elems.push(elem(x, y))
157 }
158 }
159 Grid { elems, width, height }
160 }
161 }
162
163 impl<T: Clone> Grid<T> {
164 pub fn new(width: u16, height: u16, elem: &T) -> Grid<T> {
165 let mut elems = Vec::with_capacity((width * height) as usize);
166 for _ in 0..height {
167 for _ in 0..width {
168 elems.push(elem.clone())
169 }
170 }
171 Grid { elems, width, height }
172 }
173 }
174
175 impl<T> Index<Coord> for Grid<T> {
176 type Output = T;
177
178 fn index(&self, Pair { x, y }: Coord) -> &T {
179 &self.elems[(y * self.width + x) as usize]
180 }
181 }
182
183 impl<T> IndexMut<Coord> for Grid<T> {
184 fn index_mut(&mut self, Pair { x, y }: Coord) -> &mut T {
185 &mut self.elems[(y * self.width + x) as usize]
186 }
187 }
188
189 #[derive(Debug)]
190 pub struct Board {
191 base: Grid<Tile>,
192 entity: Grid<Vec<Entity>>,
193 }
194
195
196 impl Board {
197 pub fn passable(&self, c: Coord) -> bool {
198 self.base[c].passable()
199 }
200
201 pub fn tiles(&mut self) -> &mut Grid<Tile> {
202 &mut self.base
203 }
204
205 pub fn entities(&mut self) -> &mut Grid<Vec<Entity>> {
206 &mut self.entity
207 }
208
209 pub fn get_entity(&self, c: Coord) -> &Vec<Entity> {
210 &self.entity[c]
211 }
212
213 pub fn from_tiled(map: &tiled::Map) -> Board {
214 fn offset_from_gid(n: u32) -> Option<Coord> {
215 if n < 1 || n > 1024 {
216 return None;
217 }
218 let x = (n - 1) as u16 % 32;
219 let y = (n - 1) as u16 / 32;
220 Some(Pair { x, y })
221 }
222
223 let w = map.width as u16;
224 let h = map.height as u16;
225 let base = Grid::new_with(w, h, |x, y| {
226 let o = map.layers[0].tiles[y as usize][x as usize];
227 Tile { sprite: offset_from_gid(o).unwrap() }
228 });
229
230 let entity = Grid::new_with(w, h, |x, y| {
231 let _o1 = map.layers[1].tiles[y as usize][x as usize];
232 let _o2 = map.layers[1].tiles[y as usize][x as usize];
233 vec![
234 Entity::blank(),
235 Entity::blank(),
236 ]
237 });
238 Board { base, entity }
239 }
240 }
+0
-13
src/board.rs less more
1 #![allow(dead_code)]
2
3 pub struct Tile {
4 sprite: (u8, u8),
5 }
6
7 pub struct Spritesheet {
8 image: ::imagefmt::Image<u8>,
9 }
10
11 pub struct Board {
12 tiles: Vec<Tile>,
13 }
66 extern crate glutin;
77 extern crate imagefmt;
88 extern crate rodio;
9 extern crate tiled;
910
1011 pub mod board;
1112 pub mod graphics;
12 pub mod audio;
13 // pub mod audio;
1314
1415 use graphics as gfx;
1516 use graphics::vertices::V2D;
2930 /// the main loop of the game, setting up and running the window and
3031 /// event loop
3132 pub fn main_loop() -> Result<(), gfx::Error> {
32 let audio = audio::Audio::init()?;
33 let t = tiled::parse_file(
34 std::path::Path::new("/home/gdritter/projects/animaltransiro/areas/main.tmx")
35 ).unwrap();
36 let board = board::Board::from_tiled(&t);
37
38 println!("{:#?}", board);
39
40 return Ok(());
41 // let audio = audio::Audio::init()?;
3342 let window = gfx::Window::create()?;
3443
3544 let program = gfx::Program::link(vec![
6776 match k.virtual_keycode {
6877 Some(VirtualKeyCode::Q) =>
6978 return ControlFlow::Break,
70 Some(c) =>
71 audio.play(format!("{:#?}", c)),
79 Some(_) => (),
80 // audio.play(format!("{:#?}", c)),
7281 _ => (),
7382 }
7483 }