mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 13:15:54 -06:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f5ff90cb11 | |||
| a0f65b551c | |||
| 8808a1aa3b |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -94,6 +94,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -8,9 +8,10 @@ edition = "2021"
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
spin_sleep = "1.1.1"
|
||||
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_warn"]}
|
||||
tracing = { version = "0.1.37", features = ["max_level_debug", "release_max_level_debug"]}
|
||||
tracing-error = "0.2.0"
|
||||
tracing-subscriber = {version = "0.3.17", features = ["env-filter"]}
|
||||
winapi = { version = "0.3", features = ["consoleapi", "fileapi", "handleapi", "processenv", "winbase", "wincon", "winnt", "winuser", "windef", "minwindef"] }
|
||||
|
||||
[dependencies.sdl2]
|
||||
version = "0.38"
|
||||
|
||||
BIN
assets/wav/eating.wav
Normal file
BIN
assets/wav/eating.wav
Normal file
Binary file not shown.
BIN
assets/wav/waka_ka.wav
Normal file
BIN
assets/wav/waka_ka.wav
Normal file
Binary file not shown.
BIN
assets/wav/waka_wa.wav
Normal file
BIN
assets/wav/waka_wa.wav
Normal file
Binary file not shown.
78
src/game.rs
78
src/game.rs
@@ -6,6 +6,7 @@ use sdl2::render::{Texture, TextureCreator};
|
||||
use sdl2::ttf::{Font, FontStyle};
|
||||
use sdl2::video::WindowContext;
|
||||
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
||||
use tracing::event;
|
||||
|
||||
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
||||
use crate::direction::Direction;
|
||||
@@ -20,7 +21,7 @@ pub struct Game<'a> {
|
||||
power_pellet_texture: Texture<'a>,
|
||||
font: Font<'a, 'static>,
|
||||
pacman: Pacman<'a>,
|
||||
map: Rc<Map>,
|
||||
map: Rc<std::cell::RefCell<Map>>,
|
||||
debug: bool,
|
||||
score: u32,
|
||||
}
|
||||
@@ -31,7 +32,7 @@ impl Game<'_> {
|
||||
texture_creator: &'a TextureCreator<WindowContext>,
|
||||
ttf_context: &'a sdl2::ttf::Sdl2TtfContext,
|
||||
) -> Game<'a> {
|
||||
let map = Rc::new(Map::new(RAW_BOARD));
|
||||
let map = Rc::new(std::cell::RefCell::new(Map::new(RAW_BOARD)));
|
||||
let pacman_atlas = texture_creator
|
||||
.load_texture("assets/32/pacman.png")
|
||||
.expect("Could not load pacman texture");
|
||||
@@ -73,9 +74,9 @@ impl Game<'_> {
|
||||
self.debug = !self.debug;
|
||||
}
|
||||
|
||||
// Test score increase
|
||||
if keycode == Keycode::S {
|
||||
self.add_score(10);
|
||||
// Reset game
|
||||
if keycode == Keycode::R {
|
||||
self.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +84,70 @@ impl Game<'_> {
|
||||
self.score += points;
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// Reset the map to restore all pellets
|
||||
{
|
||||
let mut map = self.map.borrow_mut();
|
||||
map.reset();
|
||||
}
|
||||
|
||||
// Reset the score
|
||||
self.score = 0;
|
||||
|
||||
// Reset Pacman position (you might want to customize this)
|
||||
// For now, we'll keep Pacman where he is, but you could add:
|
||||
// self.pacman.position = Map::cell_to_pixel((1, 1));
|
||||
|
||||
event!(tracing::Level::INFO, "Game reset - map and score cleared");
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
self.pacman.tick();
|
||||
self.check_pellet_eating();
|
||||
}
|
||||
|
||||
fn check_pellet_eating(&mut self) {
|
||||
let cell_pos = self.pacman.cell_position();
|
||||
|
||||
// Check if there's a pellet at the current position
|
||||
let tile = {
|
||||
let map = self.map.borrow();
|
||||
map.get_tile((cell_pos.0 as i32, cell_pos.1 as i32))
|
||||
};
|
||||
|
||||
if let Some(tile) = tile {
|
||||
match tile {
|
||||
MapTile::Pellet => {
|
||||
// Eat the pellet and add score
|
||||
{
|
||||
let mut map = self.map.borrow_mut();
|
||||
map.set_tile((cell_pos.0 as i32, cell_pos.1 as i32), MapTile::Empty);
|
||||
}
|
||||
self.add_score(10);
|
||||
event!(
|
||||
tracing::Level::DEBUG,
|
||||
"Pellet eaten at ({}, {})",
|
||||
cell_pos.0,
|
||||
cell_pos.1
|
||||
);
|
||||
}
|
||||
MapTile::PowerPellet => {
|
||||
// Eat the power pellet and add score
|
||||
{
|
||||
let mut map = self.map.borrow_mut();
|
||||
map.set_tile((cell_pos.0 as i32, cell_pos.1 as i32), MapTile::Empty);
|
||||
}
|
||||
self.add_score(50);
|
||||
event!(
|
||||
tracing::Level::DEBUG,
|
||||
"Power pellet eaten at ({}, {})",
|
||||
cell_pos.0,
|
||||
cell_pos.1
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&mut self) {
|
||||
@@ -112,6 +175,7 @@ impl Game<'_> {
|
||||
for y in 0..BOARD_HEIGHT {
|
||||
let tile = self
|
||||
.map
|
||||
.borrow()
|
||||
.get_tile((x as i32, y as i32))
|
||||
.unwrap_or(MapTile::Empty);
|
||||
let mut color = None;
|
||||
@@ -162,6 +226,7 @@ impl Game<'_> {
|
||||
for y in 0..BOARD_HEIGHT {
|
||||
let tile = self
|
||||
.map
|
||||
.borrow()
|
||||
.get_tile((x as i32, y as i32))
|
||||
.unwrap_or(MapTile::Empty);
|
||||
|
||||
@@ -187,9 +252,8 @@ impl Game<'_> {
|
||||
}
|
||||
|
||||
fn render_score(&mut self) {
|
||||
let score = 0;
|
||||
let lives = 3;
|
||||
let score_text = format!("{:02}", score);
|
||||
let score_text = format!("{:02}", self.score);
|
||||
|
||||
let x_offset = 12;
|
||||
let y_offset = 2;
|
||||
|
||||
47
src/main.rs
47
src/main.rs
@@ -1,3 +1,5 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
use crate::constants::{WINDOW_HEIGHT, WINDOW_WIDTH};
|
||||
use crate::game::Game;
|
||||
use sdl2::event::{Event, WindowEvent};
|
||||
@@ -7,7 +9,46 @@ use tracing::event;
|
||||
use tracing_error::ErrorLayer;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
|
||||
#[cfg(windows)]
|
||||
use winapi::{
|
||||
shared::{ntdef::NULL, windef::HWND},
|
||||
um::{
|
||||
fileapi::{CreateFileA, OPEN_EXISTING},
|
||||
handleapi::INVALID_HANDLE_VALUE,
|
||||
processenv::SetStdHandle,
|
||||
winbase::{STD_ERROR_HANDLE, STD_OUTPUT_HANDLE},
|
||||
wincon::{AttachConsole, GetConsoleWindow},
|
||||
winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, GENERIC_READ, GENERIC_WRITE},
|
||||
},
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
unsafe fn attach_console() {
|
||||
if GetConsoleWindow() != std::ptr::null_mut() as HWND {
|
||||
return;
|
||||
}
|
||||
|
||||
if AttachConsole(winapi::um::wincon::ATTACH_PARENT_PROCESS) != 0 {
|
||||
let handle = CreateFileA(
|
||||
"CONOUT$\0".as_ptr() as *const i8,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
std::ptr::null_mut(),
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
NULL,
|
||||
);
|
||||
|
||||
if handle != INVALID_HANDLE_VALUE {
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, handle);
|
||||
SetStdHandle(STD_ERROR_HANDLE, handle);
|
||||
}
|
||||
}
|
||||
// Do NOT call AllocConsole here - we don't want a console when launched from Explorer
|
||||
}
|
||||
|
||||
mod animation;
|
||||
mod audio;
|
||||
mod constants;
|
||||
mod direction;
|
||||
mod entity;
|
||||
@@ -18,8 +59,14 @@ mod modulation;
|
||||
mod pacman;
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(windows)]
|
||||
unsafe {
|
||||
attach_console();
|
||||
}
|
||||
|
||||
let sdl_context = sdl2::init().unwrap();
|
||||
let video_subsystem = sdl_context.video().unwrap();
|
||||
let audio_subsystem = sdl_context.audio().unwrap();
|
||||
let ttf_context = sdl2::ttf::init().unwrap();
|
||||
|
||||
// Setup tracing
|
||||
|
||||
37
src/map.rs
37
src/map.rs
@@ -1,13 +1,14 @@
|
||||
use crate::constants::MapTile;
|
||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
||||
|
||||
pub struct Map {
|
||||
inner: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||
current: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||
default: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||
}
|
||||
|
||||
impl Map {
|
||||
pub fn new(raw_board: [&str; BOARD_HEIGHT as usize]) -> Map {
|
||||
let mut inner = [[MapTile::Empty; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize];
|
||||
let mut map = [[MapTile::Empty; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize];
|
||||
|
||||
for y in 0..BOARD_HEIGHT as usize {
|
||||
let line = raw_board[y];
|
||||
@@ -35,11 +36,23 @@ impl Map {
|
||||
_ => panic!("Unknown character in board: {}", character),
|
||||
};
|
||||
|
||||
inner[x as usize][y as usize] = tile;
|
||||
map[x as usize][y as usize] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
Map { inner: inner }
|
||||
Map {
|
||||
current: map,
|
||||
default: map.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
// Restore the map to its original state
|
||||
for x in 0..BOARD_WIDTH as usize {
|
||||
for y in 0..BOARD_HEIGHT as usize {
|
||||
self.current[x][y] = self.default[x][y];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
||||
@@ -50,7 +63,19 @@ impl Map {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.inner[x][y])
|
||||
Some(self.current[x][y])
|
||||
}
|
||||
|
||||
pub fn set_tile(&mut self, cell: (i32, i32), tile: MapTile) -> bool {
|
||||
let x = cell.0 as usize;
|
||||
let y = cell.1 as usize;
|
||||
|
||||
if x >= BOARD_WIDTH as usize || y >= BOARD_HEIGHT as usize {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.current[x][y] = tile;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use sdl2::{
|
||||
@@ -22,14 +23,18 @@ pub struct Pacman<'a> {
|
||||
pub direction: Direction,
|
||||
pub next_direction: Option<Direction>,
|
||||
pub stopped: bool,
|
||||
map: Rc<Map>,
|
||||
map: Rc<RefCell<Map>>,
|
||||
speed: u32,
|
||||
modulation: SimpleTickModulator,
|
||||
sprite: AnimatedTexture<'a>,
|
||||
}
|
||||
|
||||
impl Pacman<'_> {
|
||||
pub fn new<'a>(starting_position: (u32, u32), atlas: Texture<'a>, map: Rc<Map>) -> Pacman<'a> {
|
||||
pub fn new<'a>(
|
||||
starting_position: (u32, u32),
|
||||
atlas: Texture<'a>,
|
||||
map: Rc<RefCell<Map>>,
|
||||
) -> Pacman<'a> {
|
||||
Pacman {
|
||||
position: Map::cell_to_pixel(starting_position),
|
||||
direction: Direction::Right,
|
||||
@@ -70,6 +75,7 @@ impl Pacman<'_> {
|
||||
let proposed_next_cell = self.next_cell(self.next_direction);
|
||||
let proposed_next_tile = self
|
||||
.map
|
||||
.borrow()
|
||||
.get_tile(proposed_next_cell)
|
||||
.unwrap_or(MapTile::Empty);
|
||||
if proposed_next_tile != MapTile::Wall {
|
||||
@@ -79,7 +85,7 @@ impl Pacman<'_> {
|
||||
}
|
||||
|
||||
fn internal_position_even(&self) -> (u32, u32) {
|
||||
let (x, y ) = self.internal_position();
|
||||
let (x, y) = self.internal_position();
|
||||
((x / 2u32) * 2u32, (y / 2u32) * 2u32)
|
||||
}
|
||||
}
|
||||
@@ -115,7 +121,7 @@ impl Entity for Pacman<'_> {
|
||||
self.handle_requested_direction();
|
||||
|
||||
let next = self.next_cell(None);
|
||||
let next_tile = self.map.get_tile(next).unwrap_or(MapTile::Empty);
|
||||
let next_tile = self.map.borrow().get_tile(next).unwrap_or(MapTile::Empty);
|
||||
|
||||
if !self.stopped && next_tile == MapTile::Wall {
|
||||
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
||||
@@ -125,7 +131,7 @@ impl Entity for Pacman<'_> {
|
||||
self.stopped = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !self.stopped && self.modulation.next() {
|
||||
let speed = self.speed as i32;
|
||||
match self.direction {
|
||||
|
||||
Reference in New Issue
Block a user