mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 11:15:47 -06:00
feat: tunnel implementation, pathfinding debug mode
This commit is contained in:
@@ -32,6 +32,8 @@ pub enum MapTile {
|
|||||||
PowerPellet,
|
PowerPellet,
|
||||||
/// A starting position for an entity.
|
/// A starting position for an entity.
|
||||||
StartingPosition(u8),
|
StartingPosition(u8),
|
||||||
|
/// A tunnel tile.
|
||||||
|
Tunnel,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The raw layout of the game board, as a 2D array of characters.
|
/// The raw layout of the game board, as a 2D array of characters.
|
||||||
@@ -50,7 +52,7 @@ pub const RAW_BOARD: [&str; BOARD_HEIGHT as usize] = [
|
|||||||
" #.## 1 ##.# ",
|
" #.## 1 ##.# ",
|
||||||
" #.## ###==### ##.# ",
|
" #.## ###==### ##.# ",
|
||||||
"######.## # # ##.######",
|
"######.## # # ##.######",
|
||||||
" . #2 3 4 # . ",
|
"T . #2 3 4 # . T",
|
||||||
"######.## # # ##.######",
|
"######.## # # ##.######",
|
||||||
" #.## ######## ##.# ",
|
" #.## ######## ##.# ",
|
||||||
" #.## ##.# ",
|
" #.## ##.# ",
|
||||||
|
|||||||
70
src/game.rs
70
src/game.rs
@@ -2,6 +2,7 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use rand::seq::IteratorRandom;
|
||||||
use sdl2::image::LoadTexture;
|
use sdl2::image::LoadTexture;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::render::{Texture, TextureCreator};
|
use sdl2::render::{Texture, TextureCreator};
|
||||||
@@ -34,6 +35,13 @@ static GHOST_EYES_TEXTURE_DATA: &[u8] = include_bytes!("../assets/32/ghost_eyes.
|
|||||||
///
|
///
|
||||||
/// This struct contains all the information necessary to run the game, including
|
/// This struct contains all the information necessary to run the game, including
|
||||||
/// the canvas, textures, fonts, game objects, and the current score.
|
/// the canvas, textures, fonts, game objects, and the current score.
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DebugMode {
|
||||||
|
None,
|
||||||
|
Grid,
|
||||||
|
Pathfinding,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Game<'a> {
|
pub struct Game<'a> {
|
||||||
canvas: &'a mut Canvas<Window>,
|
canvas: &'a mut Canvas<Window>,
|
||||||
map_texture: Texture<'a>,
|
map_texture: Texture<'a>,
|
||||||
@@ -42,7 +50,7 @@ pub struct Game<'a> {
|
|||||||
font: Font<'a, 'static>,
|
font: Font<'a, 'static>,
|
||||||
pacman: Rc<RefCell<Pacman<'a>>>,
|
pacman: Rc<RefCell<Pacman<'a>>>,
|
||||||
map: Rc<std::cell::RefCell<Map>>,
|
map: Rc<std::cell::RefCell<Map>>,
|
||||||
debug: bool,
|
debug_mode: DebugMode,
|
||||||
score: u32,
|
score: u32,
|
||||||
audio: Audio,
|
audio: Audio,
|
||||||
// Add ghost
|
// Add ghost
|
||||||
@@ -120,7 +128,7 @@ impl Game<'_> {
|
|||||||
Game {
|
Game {
|
||||||
canvas,
|
canvas,
|
||||||
pacman,
|
pacman,
|
||||||
debug: false,
|
debug_mode: DebugMode::None,
|
||||||
map,
|
map,
|
||||||
map_texture,
|
map_texture,
|
||||||
pellet_texture,
|
pellet_texture,
|
||||||
@@ -144,7 +152,11 @@ impl Game<'_> {
|
|||||||
|
|
||||||
// Toggle debug mode
|
// Toggle debug mode
|
||||||
if keycode == Keycode::Space {
|
if keycode == Keycode::Space {
|
||||||
self.debug = !self.debug;
|
self.debug_mode = match self.debug_mode {
|
||||||
|
DebugMode::None => DebugMode::Grid,
|
||||||
|
DebugMode::Grid => DebugMode::Pathfinding,
|
||||||
|
DebugMode::Pathfinding => DebugMode::None,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset game
|
// Reset game
|
||||||
@@ -173,18 +185,40 @@ impl Game<'_> {
|
|||||||
// Reset the score
|
// Reset the score
|
||||||
self.score = 0;
|
self.score = 0;
|
||||||
|
|
||||||
// Reset Pac-Man position
|
// Reset Pacman position
|
||||||
let mut pacman = self.pacman.borrow_mut();
|
let mut pacman = self.pacman.borrow_mut();
|
||||||
pacman.pixel_position = Map::cell_to_pixel((1, 1));
|
pacman.pixel_position = Map::cell_to_pixel((1, 1));
|
||||||
pacman.cell_position = (1, 1);
|
pacman.cell_position = (1, 1);
|
||||||
|
pacman.in_tunnel = false;
|
||||||
|
pacman.direction = Direction::Right;
|
||||||
|
pacman.next_direction = None;
|
||||||
|
pacman.stopped = false;
|
||||||
|
|
||||||
// Reset ghost positions
|
// Reset ghost positions and mode
|
||||||
self.blinky.set_mode(crate::ghost::GhostMode::House);
|
let mut rng = rand::rng();
|
||||||
self.blinky.pixel_position = Map::cell_to_pixel((13, 11));
|
let map = self.map.borrow();
|
||||||
self.blinky.cell_position = (13, 11);
|
let mut valid_positions = vec![];
|
||||||
self.blinky.direction = Direction::Left;
|
for x in 1..(crate::constants::BOARD_WIDTH - 1) {
|
||||||
|
for y in 1..(crate::constants::BOARD_HEIGHT - 1) {
|
||||||
|
let tile_option = map.get_tile((x as i32, y as i32));
|
||||||
|
|
||||||
event!(tracing::Level::INFO, "Game reset - map and score cleared");
|
if let Some(tile) = tile_option {
|
||||||
|
match tile {
|
||||||
|
MapTile::Empty | MapTile::Pellet | MapTile::PowerPellet => {
|
||||||
|
valid_positions.push((x, y));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(&(gx, gy)) = valid_positions.iter().choose(&mut rng) {
|
||||||
|
self.blinky.pixel_position = Map::cell_to_pixel((gx, gy));
|
||||||
|
self.blinky.cell_position = (gx, gy);
|
||||||
|
self.blinky.in_tunnel = false;
|
||||||
|
self.blinky.direction = Direction::Left;
|
||||||
|
self.blinky.mode = crate::ghost::GhostMode::Chase;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advances the game by one tick.
|
/// Advances the game by one tick.
|
||||||
@@ -275,7 +309,7 @@ impl Game<'_> {
|
|||||||
self.render_ui();
|
self.render_ui();
|
||||||
|
|
||||||
// Draw the debug grid
|
// Draw the debug grid
|
||||||
if self.debug {
|
if self.debug_mode == DebugMode::Grid {
|
||||||
for x in 0..BOARD_WIDTH {
|
for x in 0..BOARD_WIDTH {
|
||||||
for y in 0..BOARD_HEIGHT {
|
for y in 0..BOARD_HEIGHT {
|
||||||
let tile = self
|
let tile = self
|
||||||
@@ -294,6 +328,7 @@ impl Game<'_> {
|
|||||||
MapTile::Pellet => Some(Color::RED),
|
MapTile::Pellet => Some(Color::RED),
|
||||||
MapTile::PowerPellet => Some(Color::MAGENTA),
|
MapTile::PowerPellet => Some(Color::MAGENTA),
|
||||||
MapTile::StartingPosition(_) => Some(Color::GREEN),
|
MapTile::StartingPosition(_) => Some(Color::GREEN),
|
||||||
|
MapTile::Tunnel => Some(Color::CYAN),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,6 +343,19 @@ impl Game<'_> {
|
|||||||
self.draw_cell((next_cell.0 as u32, next_cell.1 as u32), Color::YELLOW);
|
self.draw_cell((next_cell.0 as u32, next_cell.1 as u32), Color::YELLOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pathfinding debug mode
|
||||||
|
if self.debug_mode == DebugMode::Pathfinding {
|
||||||
|
// Show the current path for Blinky
|
||||||
|
if let Some((path, _)) = self.blinky.get_path_to_target({
|
||||||
|
let (tx, ty) = self.blinky.get_target_tile();
|
||||||
|
(tx as u32, ty as u32)
|
||||||
|
}) {
|
||||||
|
for &(x, y) in &path {
|
||||||
|
self.draw_cell((x, y), Color::YELLOW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Present the canvas
|
// Present the canvas
|
||||||
self.canvas.present();
|
self.canvas.present();
|
||||||
}
|
}
|
||||||
|
|||||||
97
src/ghost.rs
97
src/ghost.rs
@@ -1,4 +1,4 @@
|
|||||||
use pathfinding::prelude::astar;
|
use pathfinding::prelude::dijkstra;
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
pixels::Color,
|
pixels::Color,
|
||||||
render::{Canvas, Texture},
|
render::{Canvas, Texture},
|
||||||
@@ -11,7 +11,7 @@ use rand::Rng;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
animation::AnimatedTexture,
|
animation::AnimatedTexture,
|
||||||
constants::{MapTile, BOARD_OFFSET, CELL_SIZE},
|
constants::{MapTile, BOARD_OFFSET, BOARD_WIDTH, CELL_SIZE},
|
||||||
direction::Direction,
|
direction::Direction,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
map::Map,
|
map::Map,
|
||||||
@@ -67,8 +67,6 @@ pub struct Ghost<'a> {
|
|||||||
pub mode: GhostMode,
|
pub mode: GhostMode,
|
||||||
/// The type/personality of this ghost
|
/// The type/personality of this ghost
|
||||||
pub ghost_type: GhostType,
|
pub ghost_type: GhostType,
|
||||||
/// Whether the ghost is currently blue (frightened)
|
|
||||||
pub is_blue: bool,
|
|
||||||
/// Reference to the game map
|
/// Reference to the game map
|
||||||
pub map: Rc<RefCell<Map>>,
|
pub map: Rc<RefCell<Map>>,
|
||||||
/// Reference to Pac-Man for targeting
|
/// Reference to Pac-Man for targeting
|
||||||
@@ -81,6 +79,8 @@ pub struct Ghost<'a> {
|
|||||||
body_sprite: AnimatedTexture<'a>,
|
body_sprite: AnimatedTexture<'a>,
|
||||||
/// Ghost eyes sprite
|
/// Ghost eyes sprite
|
||||||
eyes_sprite: AnimatedTexture<'a>,
|
eyes_sprite: AnimatedTexture<'a>,
|
||||||
|
/// Whether the ghost is currently in a tunnel
|
||||||
|
pub in_tunnel: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ghost<'_> {
|
impl Ghost<'_> {
|
||||||
@@ -103,13 +103,13 @@ impl Ghost<'_> {
|
|||||||
direction: Direction::Left,
|
direction: Direction::Left,
|
||||||
mode: GhostMode::Chase,
|
mode: GhostMode::Chase,
|
||||||
ghost_type,
|
ghost_type,
|
||||||
is_blue: false,
|
|
||||||
map,
|
map,
|
||||||
pacman,
|
pacman,
|
||||||
speed: 3,
|
speed: 3,
|
||||||
modulation: SimpleTickModulator::new(1.0),
|
modulation: SimpleTickModulator::new(1.0),
|
||||||
body_sprite,
|
body_sprite,
|
||||||
eyes_sprite: AnimatedTexture::new(eyes_texture, 1, 4, 32, 32, Some((-4, -4))),
|
eyes_sprite: AnimatedTexture::new(eyes_texture, 1, 4, 32, 32, Some((-4, -4))),
|
||||||
|
in_tunnel: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,14 +150,23 @@ impl Ghost<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the path to the target tile using the A* algorithm.
|
/// Calculates the path to the target tile using the A* algorithm.
|
||||||
fn get_path_to_target(&self, target: (u32, u32)) -> Option<(Vec<(u32, u32)>, u32)> {
|
pub fn get_path_to_target(&self, target: (u32, u32)) -> Option<(Vec<(u32, u32)>, u32)> {
|
||||||
let start = self.cell_position;
|
let start = self.cell_position;
|
||||||
let map = self.map.borrow();
|
let map = self.map.borrow();
|
||||||
|
|
||||||
astar(
|
dijkstra(
|
||||||
&start,
|
&start,
|
||||||
|&p| {
|
|&p| {
|
||||||
let mut successors = vec![];
|
let mut successors = vec![];
|
||||||
|
let tile = map.get_tile((p.0 as i32, p.1 as i32));
|
||||||
|
// Tunnel wrap: if currently in a tunnel, add the opposite exit as a neighbor
|
||||||
|
if let Some(MapTile::Tunnel) = tile {
|
||||||
|
if p.0 == 0 {
|
||||||
|
successors.push(((BOARD_WIDTH - 2, p.1), 1));
|
||||||
|
} else if p.0 == BOARD_WIDTH - 1 {
|
||||||
|
successors.push(((1, p.1), 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
for dir in &[
|
for dir in &[
|
||||||
Direction::Up,
|
Direction::Up,
|
||||||
Direction::Down,
|
Direction::Down,
|
||||||
@@ -167,16 +176,14 @@ impl Ghost<'_> {
|
|||||||
let (dx, dy) = dir.offset();
|
let (dx, dy) = dir.offset();
|
||||||
let next_p = (p.0 as i32 + dx, p.1 as i32 + dy);
|
let next_p = (p.0 as i32 + dx, p.1 as i32 + dy);
|
||||||
if let Some(tile) = map.get_tile(next_p) {
|
if let Some(tile) = map.get_tile(next_p) {
|
||||||
if tile != MapTile::Wall {
|
if tile == MapTile::Wall {
|
||||||
successors.push(((next_p.0 as u32, next_p.1 as u32), 1));
|
continue;
|
||||||
}
|
}
|
||||||
|
successors.push(((next_p.0 as u32, next_p.1 as u32), 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
successors
|
successors
|
||||||
},
|
},
|
||||||
|&p| {
|
|
||||||
((p.0 as i32 - target.0 as i32).abs() + (p.1 as i32 - target.1 as i32).abs()) as u32
|
|
||||||
},
|
|
||||||
|&p| p == target,
|
|&p| p == target,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -304,25 +311,53 @@ impl Entity for Ghost<'_> {
|
|||||||
(self.pixel_position.1 as u32 / CELL_SIZE) - BOARD_OFFSET.1,
|
(self.pixel_position.1 as u32 / CELL_SIZE) - BOARD_OFFSET.1,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Pathfinding logic
|
let current_tile = self
|
||||||
let target_tile = self.get_target_tile();
|
.map
|
||||||
if let Some((path, _)) =
|
.borrow()
|
||||||
self.get_path_to_target((target_tile.0 as u32, target_tile.1 as u32))
|
.get_tile((self.cell_position.0 as i32, self.cell_position.1 as i32))
|
||||||
{
|
.unwrap_or(MapTile::Empty);
|
||||||
if path.len() > 1 {
|
if current_tile == MapTile::Tunnel {
|
||||||
let next_move = path[1];
|
self.in_tunnel = true;
|
||||||
let (x, y) = self.cell_position;
|
}
|
||||||
let dx = next_move.0 as i32 - x as i32;
|
|
||||||
let dy = next_move.1 as i32 - y as i32;
|
// Tunnel logic: if in tunnel, force movement and prevent direction change
|
||||||
self.direction = if dx > 0 {
|
if self.in_tunnel {
|
||||||
Direction::Right
|
// If out of bounds, teleport to the opposite side and exit tunnel
|
||||||
} else if dx < 0 {
|
if self.cell_position.0 == 0 {
|
||||||
Direction::Left
|
self.cell_position.0 = BOARD_WIDTH - 2;
|
||||||
} else if dy > 0 {
|
self.pixel_position =
|
||||||
Direction::Down
|
Map::cell_to_pixel((self.cell_position.0, self.cell_position.1));
|
||||||
} else {
|
self.in_tunnel = false;
|
||||||
Direction::Up
|
} else if self.cell_position.0 == BOARD_WIDTH - 1 {
|
||||||
};
|
self.cell_position.0 = 1;
|
||||||
|
self.pixel_position =
|
||||||
|
Map::cell_to_pixel((self.cell_position.0, self.cell_position.1));
|
||||||
|
self.in_tunnel = false;
|
||||||
|
} else {
|
||||||
|
// While in tunnel, do not allow direction change
|
||||||
|
// and always move in the current direction
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pathfinding logic (only if not in tunnel)
|
||||||
|
let target_tile = self.get_target_tile();
|
||||||
|
if let Some((path, _)) =
|
||||||
|
self.get_path_to_target((target_tile.0 as u32, target_tile.1 as u32))
|
||||||
|
{
|
||||||
|
if path.len() > 1 {
|
||||||
|
let next_move = path[1];
|
||||||
|
let (x, y) = self.cell_position;
|
||||||
|
let dx = next_move.0 as i32 - x as i32;
|
||||||
|
let dy = next_move.1 as i32 - y as i32;
|
||||||
|
self.direction = if dx > 0 {
|
||||||
|
Direction::Right
|
||||||
|
} else if dx < 0 {
|
||||||
|
Direction::Left
|
||||||
|
} else if dy > 0 {
|
||||||
|
Direction::Down
|
||||||
|
} else {
|
||||||
|
Direction::Up
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
src/map.rs
13
src/map.rs
@@ -2,6 +2,18 @@
|
|||||||
use crate::constants::{MapTile, BOARD_OFFSET, CELL_SIZE};
|
use crate::constants::{MapTile, BOARD_OFFSET, CELL_SIZE};
|
||||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct Position(pub u32, pub u32);
|
||||||
|
|
||||||
|
impl Position {
|
||||||
|
pub fn as_i32(&self) -> (i32, i32) {
|
||||||
|
(self.0 as i32, self.1 as i32)
|
||||||
|
}
|
||||||
|
pub fn wrapping_add(&self, dx: i32, dy: i32) -> Position {
|
||||||
|
Position((self.0 as i32 + dx) as u32, (self.1 as i32 + dy) as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The game map.
|
/// The game map.
|
||||||
///
|
///
|
||||||
/// The map is represented as a 2D array of `MapTile`s. It also stores a copy of
|
/// The map is represented as a 2D array of `MapTile`s. It also stores a copy of
|
||||||
@@ -41,6 +53,7 @@ impl Map {
|
|||||||
'.' => MapTile::Pellet,
|
'.' => MapTile::Pellet,
|
||||||
'o' => MapTile::PowerPellet,
|
'o' => MapTile::PowerPellet,
|
||||||
' ' => MapTile::Empty,
|
' ' => MapTile::Empty,
|
||||||
|
'T' => MapTile::Tunnel,
|
||||||
c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => {
|
c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => {
|
||||||
MapTile::StartingPosition(c.to_digit(10).unwrap() as u8)
|
MapTile::StartingPosition(c.to_digit(10).unwrap() as u8)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use tracing::event;
|
|||||||
use crate::{
|
use crate::{
|
||||||
animation::AnimatedTexture,
|
animation::AnimatedTexture,
|
||||||
constants::MapTile,
|
constants::MapTile,
|
||||||
constants::{BOARD_OFFSET, CELL_SIZE},
|
constants::{BOARD_OFFSET, BOARD_WIDTH, CELL_SIZE},
|
||||||
direction::Direction,
|
direction::Direction,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
map::Map,
|
map::Map,
|
||||||
@@ -35,6 +35,7 @@ pub struct Pacman<'a> {
|
|||||||
speed: u32,
|
speed: u32,
|
||||||
modulation: SimpleTickModulator,
|
modulation: SimpleTickModulator,
|
||||||
sprite: AnimatedTexture<'a>,
|
sprite: AnimatedTexture<'a>,
|
||||||
|
pub in_tunnel: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pacman<'_> {
|
impl Pacman<'_> {
|
||||||
@@ -60,6 +61,7 @@ impl Pacman<'_> {
|
|||||||
stopped: false,
|
stopped: false,
|
||||||
modulation: SimpleTickModulator::new(1.0),
|
modulation: SimpleTickModulator::new(1.0),
|
||||||
sprite: AnimatedTexture::new(atlas, 2, 3, 32, 32, Some((-4, -4))),
|
sprite: AnimatedTexture::new(atlas, 2, 3, 32, 32, Some((-4, -4))),
|
||||||
|
in_tunnel: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,42 +173,58 @@ impl Entity for Pacman<'_> {
|
|||||||
// Pac-Man can only change direction when he is perfectly aligned with the grid.
|
// Pac-Man can only change direction when he is perfectly aligned with the grid.
|
||||||
let can_change = self.internal_position_even() == (0, 0);
|
let can_change = self.internal_position_even() == (0, 0);
|
||||||
|
|
||||||
if let Some(next_direction) = self.next_direction {
|
if can_change {
|
||||||
if next_direction == self.direction.opposite() {
|
self.cell_position = (
|
||||||
let next_tile_position = self.next_cell(Some(next_direction));
|
(self.pixel_position.0 as u32 / CELL_SIZE) - BOARD_OFFSET.0,
|
||||||
|
(self.pixel_position.1 as u32 / CELL_SIZE) - BOARD_OFFSET.1,
|
||||||
|
);
|
||||||
|
|
||||||
|
let current_tile = self
|
||||||
|
.map
|
||||||
|
.borrow()
|
||||||
|
.get_tile((self.cell_position.0 as i32, self.cell_position.1 as i32))
|
||||||
|
.unwrap_or(MapTile::Empty);
|
||||||
|
if current_tile == MapTile::Tunnel {
|
||||||
|
self.in_tunnel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tunnel logic: if in tunnel, force movement and prevent direction change
|
||||||
|
if self.in_tunnel {
|
||||||
|
// If out of bounds, teleport to the opposite side and exit tunnel
|
||||||
|
if self.cell_position.0 == 0 {
|
||||||
|
self.cell_position.0 = BOARD_WIDTH - 2;
|
||||||
|
self.pixel_position =
|
||||||
|
Map::cell_to_pixel((self.cell_position.0 + 1, self.cell_position.1));
|
||||||
|
self.in_tunnel = false;
|
||||||
|
} else if self.cell_position.0 == BOARD_WIDTH - 1 {
|
||||||
|
self.cell_position.0 = 1;
|
||||||
|
self.pixel_position =
|
||||||
|
Map::cell_to_pixel((self.cell_position.0 - 1, self.cell_position.1));
|
||||||
|
self.in_tunnel = false;
|
||||||
|
} else {
|
||||||
|
// While in tunnel, do not allow direction change
|
||||||
|
// and always move in the current direction
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle direction change as normal
|
||||||
|
self.handle_direction_change();
|
||||||
|
|
||||||
|
// Check if the next tile in the current direction is a wall.
|
||||||
|
let next_tile_position = self.next_cell(None);
|
||||||
let next_tile = self
|
let next_tile = self
|
||||||
.map
|
.map
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_tile(next_tile_position)
|
.get_tile(next_tile_position)
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
|
|
||||||
if next_tile != MapTile::Wall {
|
if !self.stopped && next_tile == MapTile::Wall {
|
||||||
self.direction = next_direction;
|
self.stopped = true;
|
||||||
self.next_direction = None;
|
} else if self.stopped && next_tile != MapTile::Wall {
|
||||||
|
self.stopped = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_change {
|
|
||||||
self.handle_direction_change();
|
|
||||||
|
|
||||||
// Check if the next tile in the current direction is a wall.
|
|
||||||
let next_tile_position = self.next_cell(None);
|
|
||||||
let next_tile = self
|
|
||||||
.map
|
|
||||||
.borrow()
|
|
||||||
.get_tile(next_tile_position)
|
|
||||||
.unwrap_or(MapTile::Empty);
|
|
||||||
|
|
||||||
if !self.stopped && next_tile == MapTile::Wall {
|
|
||||||
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
|
||||||
self.stopped = true;
|
|
||||||
} else if self.stopped && next_tile != MapTile::Wall {
|
|
||||||
event!(tracing::Level::DEBUG, "Wall collision resolved. Moving.");
|
|
||||||
self.stopped = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.stopped {
|
if !self.stopped {
|
||||||
if self.modulation.next() {
|
if self.modulation.next() {
|
||||||
let speed = self.speed as i32;
|
let speed = self.speed as i32;
|
||||||
|
|||||||
Reference in New Issue
Block a user