player.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. use fourteen_screws::{ Camera, Scene };
  2. use fourteen_screws::maths;
  3. use fourteen_screws::maths::{ ToFixedPoint, FromFixedPoint };
  4. use fourteen_screws::trig;
  5. use fourteen_screws::Tile;
  6. use wasm_bindgen::prelude::*;
  7. use web_sys;
  8. macro_rules! log {
  9. ( $( $t:tt )* ) => {
  10. web_sys::console::log_1(&format!( $( $t )* ).into());
  11. }
  12. }
  13. #[derive(PartialEq)]
  14. pub enum HitResult {
  15. Nothing,
  16. SlideX,
  17. SlideY,
  18. WallX,
  19. WallY,
  20. }
  21. pub struct Player {
  22. pub camera: Camera,
  23. move_speed: i32,
  24. rotate_speed: i32,
  25. margin: i32,
  26. }
  27. impl Player {
  28. pub fn new(camera: Camera, move_speed: i32, rotate_speed: i32, margin: i32) -> Player {
  29. Player { camera, move_speed, rotate_speed, margin }
  30. }
  31. fn translate(&mut self, mut direction: i32, amount: i32, scene: &Scene) -> HitResult {
  32. while direction >= trig::ANGLE_360 { direction -= trig::ANGLE_360; }
  33. while direction < trig::ANGLE_0 { direction += trig::ANGLE_360; }
  34. let xp = self.camera.x();
  35. let yp = self.camera.y();
  36. let half_tile = fourteen_screws::TILE_SIZE >> 1;
  37. // get bounds of the tile player currently occupies
  38. let x_left = xp & 0xFFC0;
  39. let y_top = yp & 0xFFC0;
  40. let x_right = x_left + fourteen_screws::TILE_SIZE;
  41. let y_bottom = y_top + fourteen_screws::TILE_SIZE;
  42. let mut hit_result = HitResult::Nothing;
  43. let mut x1 = xp + maths::mul(trig::cos(direction), amount.to_fp()).to_i32();
  44. let mut y1 = yp + maths::mul(trig::sin(direction), amount.to_fp()).to_i32();
  45. let grid_x = x_left / fourteen_screws::TILE_SIZE;
  46. let grid_y = y_top / fourteen_screws::TILE_SIZE;
  47. if x1 < xp { // are we moving left
  48. if let Tile::Wall(wall) = scene.x_wall(grid_x, grid_y) {
  49. if !wall.passable && (x1 < x_left || (x1 - x_left).abs() < self.margin) { // we crossed the wall or we're too close
  50. log!("Blocked Left");
  51. x1 = xp;
  52. hit_result = HitResult::SlideX;
  53. }
  54. }
  55. }
  56. if x1 > xp { // are we moving right
  57. if let Tile::Wall(wall) = scene.x_wall(grid_x + 1, grid_y) { // wall found in current square (right edge)
  58. if !wall.passable && (x1 > x_right || (x_right - x1).abs() < self.margin) { // we crossed the wall or we're too close
  59. x1 = xp;
  60. hit_result = HitResult::SlideX;
  61. }
  62. } else if let Tile::OutOfBounds = scene.x_wall(grid_x + 1, grid_y) {
  63. log!("TILE IS OUT OF BOUNDS");
  64. }
  65. }
  66. if y1 < yp { // are we moving up
  67. if let Tile::Wall(wall) = scene.y_wall(grid_x, grid_y) {
  68. if !wall.passable && (y1 < y_top || (y1 - y_top).abs() < self.margin) {
  69. log!("Blocked Up");
  70. y1 = yp;
  71. hit_result = HitResult::SlideY;
  72. }
  73. }
  74. }
  75. if y1 > yp { // are we moving down
  76. if let Tile::Wall(wall) = scene.y_wall(grid_x, grid_y + 1) {
  77. if !wall.passable && (y1 > y_bottom || (y_bottom - y1).abs() < self.margin) {
  78. log!("Blocked Down");
  79. y1 = yp;
  80. hit_result = HitResult::SlideY;
  81. }
  82. }
  83. }
  84. // A wall or object hasn't been hit yet. We must look further.
  85. // The current grid square will be divided into four regions:
  86. // A = top left; B = top right; C = bottom left; D = bottom right
  87. // Each of these regions will be checked to see if the player's new position
  88. // (x1, y1) is close to a wall or object that borders one of these regions.
  89. // Each grid square is 64x64 units, so each region to check is 32x32 units
  90. if hit_result == HitResult::Nothing {
  91. if y1 < (y_top + half_tile) { // new y position falls in top half
  92. // check region A-top left area of grid
  93. if x1 < x_left + half_tile { // new x position falls in left half
  94. // check adjacent x wall (to left)
  95. if let Tile::Wall(wall) = scene.x_wall(grid_x, grid_y - 1) {
  96. if !wall.passable && y1 < (y_top + self.margin) { // adjacent x wall found and new y coord is within 28 units
  97. if x1 < x_left + self.margin {
  98. if xp > x_left + (self.margin - 1) {
  99. x1 = xp;
  100. hit_result = HitResult::SlideX;
  101. } else {
  102. y1 = yp;
  103. hit_result = HitResult::SlideY;
  104. }
  105. }
  106. }
  107. }
  108. // check adjacent y wall (above)
  109. if let Tile::Wall(wall) = scene.y_wall(grid_x - 1, grid_y) {
  110. if !wall.passable && x1 < x_left + self.margin {
  111. if y1 < y_top + self.margin {
  112. if yp > y_top + (self.margin - 1) {
  113. y1 = yp;
  114. hit_result = HitResult::SlideY;
  115. } else {
  116. x1 = xp;
  117. hit_result = HitResult::SlideX;
  118. }
  119. }
  120. }
  121. }
  122. }
  123. // check region B-top right area
  124. if x1 > x_right - half_tile && hit_result == HitResult::Nothing {
  125. // check adjacent x wall (to right)
  126. if let Tile::Wall(wall) = scene.x_wall(grid_x + 1, grid_y - 1) {
  127. if !wall.passable && y1 < y_top + self.margin {
  128. if x1 > x_right - self.margin {
  129. if xp < x_right - (self.margin - 1) {
  130. x1 = xp;
  131. hit_result = HitResult::SlideX;
  132. } else {
  133. y1 = yp;
  134. hit_result = HitResult::SlideY;
  135. }
  136. }
  137. }
  138. }
  139. // check adjacent y wall (above)
  140. if let Tile::Wall(wall) = scene.y_wall(grid_x + 1, grid_y) {
  141. if !wall.passable && x1 > x_right - self.margin {
  142. if y1 < y_top + self.margin {
  143. if yp < y_top + (self.margin - 1) {
  144. y1 = yp;
  145. hit_result = HitResult::SlideY;
  146. } else {
  147. x1 = xp;
  148. hit_result = HitResult::SlideX;
  149. }
  150. }
  151. }
  152. }
  153. }
  154. }
  155. // check region C-bottom left area
  156. if y1 > y_top + half_tile && hit_result == HitResult::Nothing {
  157. if x1 < x_left + half_tile {
  158. // check adjacent x wall (to left)
  159. if let Tile::Wall(wall) = scene.x_wall(grid_x, grid_y + 1) {
  160. if !wall.passable && y1 > y_bottom - self.margin {
  161. if x1 < x_left + self.margin {
  162. if xp > x_left + (self.margin - 1) {
  163. x1 = xp;
  164. hit_result = HitResult::SlideX;
  165. } else {
  166. y1 = yp;
  167. hit_result = HitResult::SlideY;
  168. }
  169. }
  170. }
  171. }
  172. // check adjacent y wall (below)
  173. if let Tile::Wall(wall) = scene.y_wall(grid_x - 1, grid_y + 1) {
  174. if !wall.passable && x1 < x_left + self.margin {
  175. if y1 > y_bottom - self.margin {
  176. if yp < y_bottom - (self.margin - 1) {
  177. y1 = yp;
  178. hit_result = HitResult::SlideY;
  179. } else {
  180. x1 = xp;
  181. hit_result = HitResult::SlideX;
  182. }
  183. }
  184. }
  185. }
  186. }
  187. // check region D-bottom right area
  188. if x1 > x_right - half_tile && hit_result == HitResult::Nothing {
  189. // check adjacent x wall (to right)
  190. if let Tile::Wall(wall) = scene.x_wall(grid_x + 1, grid_y + 1) {
  191. if !wall.passable && y1 > y_bottom - self.margin {
  192. if x1 > x_right - self.margin {
  193. if xp < x_right - (self.margin - 1) {
  194. x1 = xp;
  195. hit_result = HitResult::SlideX;
  196. } else {
  197. y1 = yp;
  198. hit_result = HitResult::SlideY;
  199. }
  200. }
  201. }
  202. }
  203. // check adjacent y wall (below)
  204. if let Tile::Wall(wall) = scene.y_wall(grid_x + 1, grid_y + 1) {
  205. if !wall.passable && x1 > x_right - self.margin {
  206. if y1 > y_bottom - self.margin {
  207. if yp < y_bottom - (self.margin - 1) {
  208. y1 = yp;
  209. hit_result = HitResult::SlideY;
  210. } else {
  211. x1 = xp;
  212. hit_result = HitResult::SlideX;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. }
  219. }
  220. if hit_result == HitResult::SlideX && y1 == yp {
  221. hit_result = HitResult::WallX;
  222. }
  223. if hit_result == HitResult::SlideY && x1 == xp {
  224. hit_result = HitResult::WallY;
  225. }
  226. self.camera.move_to(x1, y1);
  227. hit_result
  228. }
  229. pub fn forward(&mut self, scene: &Scene) -> HitResult {
  230. return self.translate(self.camera.angle(), self.move_speed, scene);
  231. }
  232. pub fn back(&mut self, scene: &Scene) -> HitResult {
  233. return self.translate(self.camera.angle() + trig::ANGLE_180, self.move_speed, scene);
  234. }
  235. pub fn strafe_left(&mut self, scene: &Scene) -> HitResult {
  236. return self.translate(self.camera.angle() - trig::ANGLE_90, self.move_speed, scene);
  237. }
  238. pub fn strafe_right(&mut self, scene: &Scene) -> HitResult {
  239. return self.translate(self.camera.angle() + trig::ANGLE_90, self.move_speed, scene);
  240. }
  241. pub fn turn_left(&mut self) {
  242. self.camera.rotate(-self.rotate_speed);
  243. log!("{}", self.camera.angle());
  244. }
  245. pub fn turn_right(&mut self) {
  246. self.camera.rotate(self.rotate_speed);
  247. log!("{}", self.camera.angle());
  248. }
  249. }