mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 13:15:54 -06:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2c0df99dea | |||
| cf12a04c69 | |||
| fa7e985c0d | |||
| f5ff90cb11 | |||
| a0f65b551c | |||
| 8808a1aa3b |
@@ -1,10 +1,12 @@
|
|||||||
[target.wasm32-unknown-emscripten]
|
[target.'cfg(target_os = "emscripten")']
|
||||||
# TODO: Document what the fuck this is.
|
# TODO: Document what the fuck this is.
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"-O", "-C", "link-args=-O2 --profiling",
|
# "-O", "-C", "link-args=-O2 --profiling",
|
||||||
#"-C", "link-args=-O3 --closure 1",
|
#"-C", "link-args=-O3 --closure 1",
|
||||||
|
# "-C", "link-args=-g -gsource-map",
|
||||||
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
||||||
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']",
|
# "-C", "link-args=-sALLOW_MEMORY_GROWTH=1",
|
||||||
|
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sUSE_SDL_MIXER=2 -sUSE_OGG=1 -sUSE_SDL_GFX=2 -sUSE_SDL_TTF=2 -sSDL2_IMAGE_FORMATS=['png']",
|
||||||
# USE_OGG, USE_VORBIS for OGG/VORBIS usage
|
# USE_OGG, USE_VORBIS for OGG/VORBIS usage
|
||||||
"-C", "link-args=--preload-file assets/",
|
"-C", "link-args=--preload-file assets/",
|
||||||
]
|
]
|
||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -89,11 +89,13 @@ name = "pacman"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"libc",
|
||||||
"sdl2",
|
"sdl2",
|
||||||
"spin_sleep",
|
"spin_sleep",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-error",
|
"tracing-error",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
17
Cargo.toml
17
Cargo.toml
@@ -8,11 +8,18 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
spin_sleep = "1.1.1"
|
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-error = "0.2.0"
|
||||||
tracing-subscriber = {version = "0.3.17", features = ["env-filter"]}
|
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]
|
|
||||||
|
[target.'cfg(target_os = "emscripten")'.dependencies.sdl2]
|
||||||
|
version = "0.38"
|
||||||
|
default-features = false
|
||||||
|
features = ["ttf","image","gfx","mixer"]
|
||||||
|
|
||||||
|
[target.'cfg(not(target_os = "emscripten"))'.dependencies.sdl2]
|
||||||
version = "0.38"
|
version = "0.38"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["ttf","image","gfx","mixer","static-link","use-vcpkg"]
|
features = ["ttf","image","gfx","mixer","static-link","use-vcpkg"]
|
||||||
@@ -23,4 +30,8 @@ git = "https://github.com/microsoft/vcpkg"
|
|||||||
rev = "2024.05.24" # release 2024.05.24 # to check for a new one, check https://github.com/microsoft/vcpkg/releases
|
rev = "2024.05.24" # release 2024.05.24 # to check for a new one, check https://github.com/microsoft/vcpkg/releases
|
||||||
|
|
||||||
[package.metadata.vcpkg.target]
|
[package.metadata.vcpkg.target]
|
||||||
x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md" }
|
x86_64-pc-windows-msvc = { triplet = "x64-windows-static-md" }
|
||||||
|
stable-x86_64-unknown-linux-gnu = { triplet = "x86_64-unknown-linux-gnu" }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "emscripten")'.dependencies]
|
||||||
|
libc = "0.2.16"
|
||||||
BIN
assets/wav/1.ogg
Normal file
BIN
assets/wav/1.ogg
Normal file
Binary file not shown.
BIN
assets/wav/2.ogg
Normal file
BIN
assets/wav/2.ogg
Normal file
Binary file not shown.
BIN
assets/wav/3.ogg
Normal file
BIN
assets/wav/3.ogg
Normal file
Binary file not shown.
BIN
assets/wav/4.ogg
Normal file
BIN
assets/wav/4.ogg
Normal file
Binary file not shown.
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.
73
src/audio.rs
Normal file
73
src/audio.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use sdl2::{
|
||||||
|
mixer::{self, Chunk, InitFlag, LoaderRWops, DEFAULT_FORMAT},
|
||||||
|
rwops::RWops,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Embed sound files directly into the executable
|
||||||
|
const SOUND_1_DATA: &[u8] = include_bytes!("../assets/wav/1.ogg");
|
||||||
|
const SOUND_2_DATA: &[u8] = include_bytes!("../assets/wav/2.ogg");
|
||||||
|
const SOUND_3_DATA: &[u8] = include_bytes!("../assets/wav/3.ogg");
|
||||||
|
const SOUND_4_DATA: &[u8] = include_bytes!("../assets/wav/4.ogg");
|
||||||
|
|
||||||
|
const SOUND_DATA: [&[u8]; 4] = [SOUND_1_DATA, SOUND_2_DATA, SOUND_3_DATA, SOUND_4_DATA];
|
||||||
|
|
||||||
|
pub struct Audio {
|
||||||
|
_mixer_context: mixer::Sdl2MixerContext,
|
||||||
|
sounds: Vec<Chunk>,
|
||||||
|
next_sound_index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Audio {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let frequency = 44100;
|
||||||
|
let format = DEFAULT_FORMAT;
|
||||||
|
let channels = 4;
|
||||||
|
let chunk_size = 128;
|
||||||
|
mixer::open_audio(frequency, format, 1, chunk_size).expect("Failed to open audio");
|
||||||
|
mixer::allocate_channels(channels);
|
||||||
|
|
||||||
|
// set channel volume
|
||||||
|
for i in 0..channels {
|
||||||
|
mixer::Channel(i as i32).set_volume(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mixer_context = mixer::init(InitFlag::OGG).expect("Failed to initialize SDL2_mixer");
|
||||||
|
|
||||||
|
let sounds: Vec<Chunk> = SOUND_DATA
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, data)| {
|
||||||
|
let rwops = RWops::from_bytes(data)
|
||||||
|
.expect(&format!("Failed to create RWops for sound {}", i + 1));
|
||||||
|
rwops.load_wav().expect(&format!(
|
||||||
|
"Failed to load sound {} from embedded data",
|
||||||
|
i + 1
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Audio {
|
||||||
|
_mixer_context: mixer_context,
|
||||||
|
sounds,
|
||||||
|
next_sound_index: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eat(&mut self) {
|
||||||
|
if let Some(chunk) = self.sounds.get(self.next_sound_index) {
|
||||||
|
match mixer::Channel(0).play(chunk, 0) {
|
||||||
|
Ok(channel) => {
|
||||||
|
tracing::info!(
|
||||||
|
"Playing sound #{} on channel {:?}",
|
||||||
|
self.next_sound_index + 1,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Could not play sound #{}: {}", self.next_sound_index + 1, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.next_sound_index = (self.next_sound_index + 1) % self.sounds.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
118
src/game.rs
118
src/game.rs
@@ -3,16 +3,26 @@ use std::rc::Rc;
|
|||||||
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};
|
||||||
use sdl2::ttf::{Font, FontStyle};
|
use sdl2::rwops::RWops;
|
||||||
|
use sdl2::ttf::Font;
|
||||||
use sdl2::video::WindowContext;
|
use sdl2::video::WindowContext;
|
||||||
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
||||||
|
use tracing::event;
|
||||||
|
|
||||||
|
use crate::audio::Audio;
|
||||||
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
||||||
use crate::direction::Direction;
|
use crate::direction::Direction;
|
||||||
use crate::entity::Entity;
|
use crate::entity::Entity;
|
||||||
use crate::map::Map;
|
use crate::map::Map;
|
||||||
use crate::pacman::Pacman;
|
use crate::pacman::Pacman;
|
||||||
|
|
||||||
|
// Embed texture data directly into the executable
|
||||||
|
static PACMAN_TEXTURE_DATA: &[u8] = include_bytes!("../assets/32/pacman.png");
|
||||||
|
static PELLET_TEXTURE_DATA: &[u8] = include_bytes!("../assets/24/pellet.png");
|
||||||
|
static POWER_PELLET_TEXTURE_DATA: &[u8] = include_bytes!("../assets/24/energizer.png");
|
||||||
|
static MAP_TEXTURE_DATA: &[u8] = include_bytes!("../assets/map.png");
|
||||||
|
static FONT_DATA: &[u8] = include_bytes!("../assets/font/konami.ttf");
|
||||||
|
|
||||||
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>,
|
||||||
@@ -20,9 +30,10 @@ pub struct Game<'a> {
|
|||||||
power_pellet_texture: Texture<'a>,
|
power_pellet_texture: Texture<'a>,
|
||||||
font: Font<'a, 'static>,
|
font: Font<'a, 'static>,
|
||||||
pacman: Pacman<'a>,
|
pacman: Pacman<'a>,
|
||||||
map: Rc<Map>,
|
map: Rc<std::cell::RefCell<Map>>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
score: u32,
|
score: u32,
|
||||||
|
audio: Audio,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game<'_> {
|
impl Game<'_> {
|
||||||
@@ -30,36 +41,51 @@ impl Game<'_> {
|
|||||||
canvas: &'a mut Canvas<Window>,
|
canvas: &'a mut Canvas<Window>,
|
||||||
texture_creator: &'a TextureCreator<WindowContext>,
|
texture_creator: &'a TextureCreator<WindowContext>,
|
||||||
ttf_context: &'a sdl2::ttf::Sdl2TtfContext,
|
ttf_context: &'a sdl2::ttf::Sdl2TtfContext,
|
||||||
|
_audio_subsystem: &'a sdl2::AudioSubsystem,
|
||||||
) -> Game<'a> {
|
) -> Game<'a> {
|
||||||
let map = Rc::new(Map::new(RAW_BOARD));
|
let map = Rc::new(std::cell::RefCell::new(Map::new(RAW_BOARD)));
|
||||||
|
|
||||||
|
// Load Pacman texture from embedded data
|
||||||
let pacman_atlas = texture_creator
|
let pacman_atlas = texture_creator
|
||||||
.load_texture("assets/32/pacman.png")
|
.load_texture_bytes(PACMAN_TEXTURE_DATA)
|
||||||
.expect("Could not load pacman texture");
|
.expect("Could not load pacman texture from embedded data");
|
||||||
let pacman = Pacman::new((1, 1), pacman_atlas, Rc::clone(&map));
|
let pacman = Pacman::new((1, 1), pacman_atlas, Rc::clone(&map));
|
||||||
|
|
||||||
|
// Load pellet texture from embedded data
|
||||||
let pellet_texture = texture_creator
|
let pellet_texture = texture_creator
|
||||||
.load_texture("assets/24/pellet.png")
|
.load_texture_bytes(PELLET_TEXTURE_DATA)
|
||||||
.expect("Could not load pellet texture");
|
.expect("Could not load pellet texture from embedded data");
|
||||||
let power_pellet_texture = texture_creator
|
|
||||||
.load_texture("assets/24/energizer.png")
|
|
||||||
.expect("Could not load power pellet texture");
|
|
||||||
|
|
||||||
|
// Load power pellet texture from embedded data
|
||||||
|
let power_pellet_texture = texture_creator
|
||||||
|
.load_texture_bytes(POWER_PELLET_TEXTURE_DATA)
|
||||||
|
.expect("Could not load power pellet texture from embedded data");
|
||||||
|
|
||||||
|
// Load font from embedded data
|
||||||
|
let font_rwops = RWops::from_bytes(FONT_DATA).expect("Failed to create RWops for font");
|
||||||
let font = ttf_context
|
let font = ttf_context
|
||||||
.load_font("assets/font/konami.ttf", 24)
|
.load_font_from_rwops(font_rwops, 24)
|
||||||
.expect("Could not load font");
|
.expect("Could not load font from embedded data");
|
||||||
|
|
||||||
|
let audio = Audio::new();
|
||||||
|
|
||||||
|
// Load map texture from embedded data
|
||||||
|
let mut map_texture = texture_creator
|
||||||
|
.load_texture_bytes(MAP_TEXTURE_DATA)
|
||||||
|
.expect("Could not load map texture from embedded data");
|
||||||
|
map_texture.set_color_mod(0, 0, 255);
|
||||||
|
|
||||||
Game {
|
Game {
|
||||||
canvas,
|
canvas,
|
||||||
pacman: pacman,
|
pacman: pacman,
|
||||||
debug: false,
|
debug: false,
|
||||||
map: map,
|
map: map,
|
||||||
map_texture: texture_creator
|
map_texture,
|
||||||
.load_texture("assets/map.png")
|
|
||||||
.expect("Could not load map texture"),
|
|
||||||
pellet_texture,
|
pellet_texture,
|
||||||
power_pellet_texture,
|
power_pellet_texture,
|
||||||
font,
|
font,
|
||||||
score: 0,
|
score: 0,
|
||||||
|
audio,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,9 +99,9 @@ impl Game<'_> {
|
|||||||
self.debug = !self.debug;
|
self.debug = !self.debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test score increase
|
// Reset game
|
||||||
if keycode == Keycode::S {
|
if keycode == Keycode::R {
|
||||||
self.add_score(10);
|
self.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,8 +109,59 @@ impl Game<'_> {
|
|||||||
self.score += points;
|
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) {
|
pub fn tick(&mut self) {
|
||||||
self.pacman.tick();
|
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 {
|
||||||
|
let pellet_value = match tile {
|
||||||
|
MapTile::Pellet => Some(10),
|
||||||
|
MapTile::PowerPellet => Some(50),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(value) = pellet_value {
|
||||||
|
{
|
||||||
|
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(value);
|
||||||
|
self.audio.eat();
|
||||||
|
event!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Pellet eaten at ({}, {})",
|
||||||
|
cell_pos.0,
|
||||||
|
cell_pos.1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
@@ -112,6 +189,7 @@ impl Game<'_> {
|
|||||||
for y in 0..BOARD_HEIGHT {
|
for y in 0..BOARD_HEIGHT {
|
||||||
let tile = self
|
let tile = self
|
||||||
.map
|
.map
|
||||||
|
.borrow()
|
||||||
.get_tile((x as i32, y as i32))
|
.get_tile((x as i32, y as i32))
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
let mut color = None;
|
let mut color = None;
|
||||||
@@ -162,6 +240,7 @@ impl Game<'_> {
|
|||||||
for y in 0..BOARD_HEIGHT {
|
for y in 0..BOARD_HEIGHT {
|
||||||
let tile = self
|
let tile = self
|
||||||
.map
|
.map
|
||||||
|
.borrow()
|
||||||
.get_tile((x as i32, y as i32))
|
.get_tile((x as i32, y as i32))
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
|
|
||||||
@@ -187,9 +266,8 @@ impl Game<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn render_score(&mut self) {
|
fn render_score(&mut self) {
|
||||||
let score = 0;
|
|
||||||
let lives = 3;
|
let lives = 3;
|
||||||
let score_text = format!("{:02}", score);
|
let score_text = format!("{:02}", self.score);
|
||||||
|
|
||||||
let x_offset = 12;
|
let x_offset = 12;
|
||||||
let y_offset = 2;
|
let y_offset = 2;
|
||||||
|
|||||||
55
src/main.rs
55
src/main.rs
@@ -1,3 +1,5 @@
|
|||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
use crate::constants::{WINDOW_HEIGHT, WINDOW_WIDTH};
|
use crate::constants::{WINDOW_HEIGHT, WINDOW_WIDTH};
|
||||||
use crate::game::Game;
|
use crate::game::Game;
|
||||||
use sdl2::event::{Event, WindowEvent};
|
use sdl2::event::{Event, WindowEvent};
|
||||||
@@ -7,7 +9,46 @@ use tracing::event;
|
|||||||
use tracing_error::ErrorLayer;
|
use tracing_error::ErrorLayer;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
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 animation;
|
||||||
|
mod audio;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod direction;
|
mod direction;
|
||||||
mod entity;
|
mod entity;
|
||||||
@@ -18,8 +59,14 @@ mod modulation;
|
|||||||
mod pacman;
|
mod pacman;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
#[cfg(windows)]
|
||||||
|
unsafe {
|
||||||
|
attach_console();
|
||||||
|
}
|
||||||
|
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
|
let audio_subsystem = sdl_context.audio().unwrap();
|
||||||
let ttf_context = sdl2::ttf::init().unwrap();
|
let ttf_context = sdl2::ttf::init().unwrap();
|
||||||
|
|
||||||
// Setup tracing
|
// Setup tracing
|
||||||
@@ -47,7 +94,12 @@ pub fn main() {
|
|||||||
.expect("Could not set logical size");
|
.expect("Could not set logical size");
|
||||||
|
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
let mut game = Game::new(&mut canvas, &texture_creator, &ttf_context);
|
let mut game = Game::new(
|
||||||
|
&mut canvas,
|
||||||
|
&texture_creator,
|
||||||
|
&ttf_context,
|
||||||
|
&audio_subsystem,
|
||||||
|
);
|
||||||
|
|
||||||
let mut event_pump = sdl_context
|
let mut event_pump = sdl_context
|
||||||
.event_pump()
|
.event_pump()
|
||||||
@@ -117,6 +169,7 @@ pub fn main() {
|
|||||||
|
|
||||||
// TODO: Proper pausing implementation that does not interfere with statistic gathering
|
// TODO: Proper pausing implementation that does not interfere with statistic gathering
|
||||||
if !paused {
|
if !paused {
|
||||||
|
// game.audio_demo_tick();
|
||||||
game.tick();
|
game.tick();
|
||||||
game.draw();
|
game.draw();
|
||||||
}
|
}
|
||||||
|
|||||||
37
src/map.rs
37
src/map.rs
@@ -1,13 +1,14 @@
|
|||||||
use crate::constants::MapTile;
|
use crate::constants::MapTile;
|
||||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
||||||
|
|
||||||
pub struct Map {
|
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 {
|
impl Map {
|
||||||
pub fn new(raw_board: [&str; BOARD_HEIGHT as usize]) -> 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 {
|
for y in 0..BOARD_HEIGHT as usize {
|
||||||
let line = raw_board[y];
|
let line = raw_board[y];
|
||||||
@@ -35,11 +36,23 @@ impl Map {
|
|||||||
_ => panic!("Unknown character in board: {}", character),
|
_ => 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> {
|
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
||||||
@@ -50,7 +63,19 @@ impl Map {
|
|||||||
return None;
|
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) {
|
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
@@ -22,14 +23,18 @@ pub struct Pacman<'a> {
|
|||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
pub next_direction: Option<Direction>,
|
pub next_direction: Option<Direction>,
|
||||||
pub stopped: bool,
|
pub stopped: bool,
|
||||||
map: Rc<Map>,
|
map: Rc<RefCell<Map>>,
|
||||||
speed: u32,
|
speed: u32,
|
||||||
modulation: SimpleTickModulator,
|
modulation: SimpleTickModulator,
|
||||||
sprite: AnimatedTexture<'a>,
|
sprite: AnimatedTexture<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pacman<'_> {
|
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 {
|
Pacman {
|
||||||
position: Map::cell_to_pixel(starting_position),
|
position: Map::cell_to_pixel(starting_position),
|
||||||
direction: Direction::Right,
|
direction: Direction::Right,
|
||||||
@@ -70,16 +75,27 @@ impl Pacman<'_> {
|
|||||||
let proposed_next_cell = self.next_cell(self.next_direction);
|
let proposed_next_cell = self.next_cell(self.next_direction);
|
||||||
let proposed_next_tile = self
|
let proposed_next_tile = self
|
||||||
.map
|
.map
|
||||||
|
.borrow()
|
||||||
.get_tile(proposed_next_cell)
|
.get_tile(proposed_next_cell)
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
if proposed_next_tile != MapTile::Wall {
|
if proposed_next_tile != MapTile::Wall {
|
||||||
|
event!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Direction change: {:?} -> {:?} at position ({}, {}) internal ({}, {})",
|
||||||
|
self.direction,
|
||||||
|
self.next_direction.unwrap(),
|
||||||
|
self.position.0,
|
||||||
|
self.position.1,
|
||||||
|
self.internal_position().0,
|
||||||
|
self.internal_position().1
|
||||||
|
);
|
||||||
self.direction = self.next_direction.unwrap();
|
self.direction = self.next_direction.unwrap();
|
||||||
self.next_direction = None;
|
self.next_direction = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_position_even(&self) -> (u32, u32) {
|
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)
|
((x / 2u32) * 2u32, (y / 2u32) * 2u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +131,7 @@ impl Entity for Pacman<'_> {
|
|||||||
self.handle_requested_direction();
|
self.handle_requested_direction();
|
||||||
|
|
||||||
let next = self.next_cell(None);
|
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 {
|
if !self.stopped && next_tile == MapTile::Wall {
|
||||||
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
||||||
@@ -125,7 +141,7 @@ impl Entity for Pacman<'_> {
|
|||||||
self.stopped = false;
|
self.stopped = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.stopped && self.modulation.next() {
|
if !self.stopped && self.modulation.next() {
|
||||||
let speed = self.speed as i32;
|
let speed = self.speed as i32;
|
||||||
match self.direction {
|
match self.direction {
|
||||||
|
|||||||
Reference in New Issue
Block a user