peachy/src/bin/player.rs

309 lines
9.7 KiB
Rust

#[windows_subsystem = "windows"]
use std::collections::HashMap;
use std::path::PathBuf;
use log::error;
use pixels::{Error, SurfaceTexture, PixelsBuilder};
use pixels::wgpu::BackendBit;
use winit::dpi::{LogicalPosition, LogicalSize, PhysicalSize};
use winit::event::{Event, VirtualKeyCode};
use winit::event_loop::{ControlFlow, EventLoop};
use winit_input_helper::WinitInputHelper;
use peachy::Game;
#[derive(Clone, Debug)]
struct Image {
pixels: [u8; 64]
}
struct State {
game: Game,
player_position: (u8, u8),
player_avatar: Image,
palette: [[u8; 4]; 4],
current_music: Option<String>,
music: HashMap<String, rodio::Sink>,
}
impl State {
fn draw(&self, screen: &mut [u8]) {
// clear screen
for pixel in screen.chunks_exact_mut(4) {
pixel.copy_from_slice(&self.palette[0]);
}
let (player_x, player_y) = self.player_position;
// each row of player avatar
for (tile_y, row) in self.player_avatar.pixels.chunks(8).enumerate() {
for (tile_x, pixel) in row.iter().enumerate() {
let colour = self.palette[*pixel as usize];
for (v, value) in colour.iter().enumerate() {
screen[
(
// player vertical offset (number of lines above)
(player_y as usize * 8 * (8 * self.game.config.width as usize))
+
// player horizontal offset; number of pixels to the left
(player_x as usize * 8)
+
// tile vertical offset; number of lines within tile
(tile_y as usize * (8 * self.game.config.width as usize))
+
(tile_x as usize) // tile horizontal offset
)
* 4 // we're dealing with rgba values so multiply everything by 4
+ v // value offset: which of the rgba values?
] = value.clone();
}
}
}
}
// fn update(&self) {
//
// }
}
fn main() -> Result<(), Error> {
env_logger::init();
let path = PathBuf::from("src/test-resources/basic");
let game = peachy::Game::from_dir(path).unwrap();
let mut state = State {
game,
player_position: (8, 4),
player_avatar: Image { pixels: [
0,0,0,2,2,0,0,0,
0,0,0,2,2,0,0,0,
0,0,1,1,1,0,2,0,
0,1,1,1,1,1,0,0,
2,0,1,1,1,0,0,0,
0,0,3,3,3,0,0,0,
0,0,3,0,3,0,0,0,
0,0,3,0,3,0,0,0,
]},
palette: [
[0xff, 0x7f, 0x7f, 0xff],
[0xff, 0xb2, 0x7f, 0xff],
[0xff, 0xe9, 0x7f, 0xff],
[0x00, 0x7f, 0x7f, 0x46],
],
current_music: None,
music: HashMap::new(),
};
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let (window, p_width, p_height, mut _hidpi_factor) = create_window(
"pixels test",
(state.game.config.width * 8) as f64,
(state.game.config.height * 8) as f64,
&event_loop
);
let surface_texture = SurfaceTexture::new(p_width, p_height, &window);
let mut pixels = PixelsBuilder::new(
(state.game.config.width * 8) as u32,
(state.game.config.height * 8) as u32,
surface_texture
)
.wgpu_backend(BackendBit::GL | BackendBit::PRIMARY)
.enable_vsync(false)
.build()?;
// let device = rodio::default_output_device().unwrap();
//
// let source = rodio_xm::XMSource::from_bytes(
// include_bytes!("../ninety degrees.xm")
// );
//
// let sink = rodio::Sink::new(&device);
// sink.append(source);
// sink.pause();
//
// game.music.insert(":ninety degrees".into(), sink);
//
// let source = rodio_xm::XMSource::from_bytes(
// include_bytes!("../orn_keygentheme2001.xm")
// );
//
// let sink = rodio::Sink::new(&device);
// sink.append(source);
// sink.pause();
//
// game.music.insert("orn_keygentheme2001".into(), sink);
state.current_music = None;
event_loop.run(move |event, _, control_flow| {
// The one and only event that winit_input_helper doesn't have for us...
if let Event::RedrawRequested(_) = event {
state.draw(pixels.get_frame());
if pixels
.render()
.map_err(|e| error!("pixels.render() failed: {:?}", e))
.is_err()
{
*control_flow = ControlFlow::Exit;
return;
}
}
// For everything else, for let winit_input_helper collect events to build its state.
// It returns `true` when it is time to update our game state and request a redraw.
if input.update(&event) {
// Close events
if input.key_pressed(VirtualKeyCode::Escape) || input.quit() {
*control_flow = ControlFlow::Exit;
return;
}
if input.key_pressed(VirtualKeyCode::M) {
// pause the current tune
if state.current_music.is_some() {
state.music.get(state.current_music.as_ref().unwrap()).unwrap().pause();
}
if state.current_music.is_none() || state.current_music.as_ref().unwrap() == "orn_keygentheme2001" {
// play the first tune
state.current_music = Some(":ninety degrees".into());
state.music.get(state.current_music.as_ref().unwrap()).unwrap().play();
} else {
// play the second tune
state.current_music = Some("orn_keygentheme2001".into());
state.music.get(state.current_music.as_ref().unwrap()).unwrap().play();
}
}
if input.key_pressed(VirtualKeyCode::Left) {
let (x, y) = state.player_position;
if x > 0 {
state.player_position = (x - 1, y);
window.request_redraw();
}
}
if input.key_pressed(VirtualKeyCode::Right) {
let (x, y) = state.player_position;
if x < state.game.config.width as u8 - 1 {
state.player_position = (x + 1, y);
window.request_redraw();
}
}
if input.key_pressed(VirtualKeyCode::Up) {
let (x, y) = state.player_position;
if y > 0 {
state.player_position = (x, y - 1);
window.request_redraw();
}
}
if input.key_pressed(VirtualKeyCode::Down) {
let (x, y) = state.player_position;
if y < state.game.config.height as u8 - 1 {
state.player_position = (x, y + 1);
window.request_redraw();
}
}
// Adjust high DPI factor
if let Some(factor) = input.scale_factor_changed() {
_hidpi_factor = factor;
window.request_redraw();
}
// Resize the window
if let Some(size) = input.window_resized() {
pixels.resize_surface(size.width, size.height);
window.request_redraw();
}
*control_flow = ControlFlow::Wait;
}
});
}
#[cfg(not(target_os = "windows"))]
fn window_builder(title: &str, event_loop: &EventLoop<()>) -> winit::window::Window {
winit::window::WindowBuilder::new()
.with_visible(false)
.with_title(title).build(&event_loop).unwrap()
}
#[cfg(target_os = "windows")]
fn window_builder(title: &str, event_loop: &EventLoop<()>) -> winit::window::Window {
use winit::platform::windows::WindowBuilderExtWindows;
winit::window::WindowBuilder::new()
.with_drag_and_drop(false)
.with_visible(false)
.with_title(title).build(&event_loop).unwrap()
}
/// Create a window for the game.
///
/// Automatically scales the window to cover about 2/3 of the monitor height.
///
/// # Returns
///
/// Tuple of `(window, surface, width, height, hidpi_factor)`
/// `width` and `height` are in `PhysicalSize` units.
fn create_window(
title: &str,
width: f64,
height: f64,
event_loop: &EventLoop<()>,
) -> (winit::window::Window, u32, u32, f64) {
let window = window_builder(title, event_loop);
let hidpi_factor = window.scale_factor();
// Get dimensions
let (monitor_width, monitor_height) = {
if let Some(monitor) = window.current_monitor() {
let size = monitor.size().to_logical(hidpi_factor);
(size.width, size.height)
} else {
(width, height)
}
};
let scale = (monitor_height / height * 2.0 / 3.0).round().max(1.0);
// Resize, center, and display the window
let min_size: winit::dpi::LogicalSize<f64> =
PhysicalSize::new(width, height).to_logical(hidpi_factor);
let default_size =
LogicalSize::new(width * scale, height * scale);
let center = LogicalPosition::new(
(monitor_width - width * scale) / 2.0,
(monitor_height - height * scale) / 2.0,
);
window.set_inner_size(default_size);
// window.set_min_inner_size(Some(min_size));
window.set_outer_position(center);
window.set_visible(true);
let size = default_size.to_physical::<f64>(hidpi_factor);
(
window,
size.width.round() as u32,
size.height.round() as u32,
hidpi_factor,
)
}