diff --git a/Cargo.lock b/Cargo.lock index fdb679a..71cfb48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -830,6 +830,7 @@ dependencies = [ "anyhow", "bass-sys", "ggez", + "image", "libosu", "log", "num", @@ -1282,10 +1283,12 @@ dependencies = [ "byteorder", "color_quant", "gif", + "jpeg-decoder", "num-iter", "num-rational", "num-traits", "png", + "scoped_threadpool", "tiff", ] @@ -1364,6 +1367,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc797adac5f083b8ff0ca6f6294a999393d76e197c36488e2ef732c4715f6fa3" dependencies = [ "byteorder", + "rayon", ] [[package]] @@ -2505,6 +2509,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +[[package]] +name = "scoped_threadpool" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" + [[package]] name = "scopeguard" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 5005901..3f0bd30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ stderrlog = "0.5.1" num = "0.3.1" ordered-float = "2.0.1" structopt = "0.3.21" +image = "0.23.12" [dependencies.libosu] git = "https://github.com/iptq/libosu" diff --git a/src/game/background.rs b/src/game/background.rs index 9e8f3f5..bd46cab 100644 --- a/src/game/background.rs +++ b/src/game/background.rs @@ -1,10 +1,18 @@ use anyhow::Result; -use ggez::Context; +use ggez::{ + graphics::{self, Color, DrawParam}, + Context, +}; use super::Game; impl Game { pub(super) fn draw_background(&self, ctx: &mut Context) -> Result<()> { + if let Some(image) = &self.background_image { + let dim = Color::new(1.0, 1.0, 1.0, 0.35); + graphics::draw(ctx, image, DrawParam::default().color(dim))?; + } + Ok(()) } } diff --git a/src/game/mod.rs b/src/game/mod.rs index b74c0fd..9860174 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -12,10 +12,11 @@ use std::str::FromStr; use anyhow::Result; use ggez::{ - event::{EventHandler, KeyCode, KeyMods}, - graphics::{self, Color, DrawParam, FilterMode, Rect, Text, WHITE}, + event::{EventHandler, KeyCode, KeyMods, MouseButton}, + graphics::{self, Color, DrawParam, FilterMode, Image, Rect, Text, WHITE}, Context, GameError, GameResult, }; +use image::io::Reader as ImageReader; use libosu::{ beatmap::Beatmap, hitobject::{HitObjectKind, SpinnerInfo}, @@ -28,6 +29,7 @@ use crate::audio::{AudioEngine, Sound}; use crate::beatmap::{BeatmapExt, STACK_DISTANCE}; use crate::hitobject::HitObjectExt; use crate::skin::Skin; +use crate::utils; pub const PLAYFIELD_BOUNDS: Rect = Rect::new(112.0, 122.0, 800.0, 600.0); pub const DEFAULT_COLORS: &[(f32, f32, f32)] = &[ @@ -48,6 +50,7 @@ pub struct Game { frame: usize, slider_cache: SliderCache, combo_colors: Vec, + background_image: Option, keymap: HashSet, current_uninherited_timing_point: Option, @@ -74,6 +77,7 @@ impl Game { .iter() .map(|(r, g, b)| Color::new(*r, *g, *b, 1.0)) .collect(), + background_image: None, keymap: HashSet::new(), current_uninherited_timing_point: None, @@ -81,7 +85,7 @@ impl Game { }) } - pub fn load_beatmap(&mut self, path: impl AsRef) -> Result<()> { + pub fn load_beatmap(&mut self, ctx: &mut Context, path: impl AsRef) -> Result<()> { let path = path.as_ref(); let mut file = File::open(&path)?; @@ -118,6 +122,25 @@ impl Game { let dir = path.parent().unwrap(); + // TODO: more background images possible? + for evt in self.beatmap.inner.events.iter() { + use libosu::events::Event; + if let Event::Background(evt) = evt { + let path = utils::fuck_you_windows(dir, &evt.filename)?; + if let Some(path) = path { + let img = ImageReader::open(path)?.decode()?; + let img_buf = img.into_rgba8(); + let image = Image::from_rgba8( + ctx, + img_buf.width() as u16, + img_buf.height() as u16, + img_buf.as_raw(), + )?; + self.background_image = Some(image); + } + } + } + let song = Sound::create(dir.join(&self.beatmap.inner.audio_filename))?; self.song = Some(song); self.timestamp_changed()?; @@ -395,6 +418,8 @@ impl EventHandler for Game { Ok(()) } + fn mouse_button_down_event(&mut self, ctx: &mut Context, _: MouseButton, x: f32, y: f32) {} + fn key_up_event(&mut self, _: &mut Context, keycode: KeyCode, _: KeyMods) { use KeyCode::*; match keycode { diff --git a/src/game/timeline.rs b/src/game/timeline.rs index aec9326..fb9659f 100644 --- a/src/game/timeline.rs +++ b/src/game/timeline.rs @@ -189,7 +189,10 @@ impl Game { .with_line_width(BOUNDS.h) .with_line_cap(LineCap::Round), ), - &[Point2::from([head_x, body_y]), Point2::from([tail_x, body_y])], + &[ + Point2::from([head_x, body_y]), + Point2::from([tail_x, body_y]), + ], color, )?; graphics::draw(ctx, &body, DrawParam::default())?; diff --git a/src/main.rs b/src/main.rs index aa970ce..b34a464 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ mod beatmap; mod game; mod hitobject; mod skin; +mod utils; use std::path::PathBuf; @@ -55,7 +56,7 @@ fn main() -> Result<()> { game.skin.load_all(&mut ctx)?; if let Some(path) = opt.path { - game.load_beatmap(path)?; + game.load_beatmap(&mut ctx, path)?; } if let Some(start_time) = opt.start_time { diff --git a/src/skin.rs b/src/skin.rs index c8e467a..00844f1 100644 --- a/src/skin.rs +++ b/src/skin.rs @@ -92,9 +92,7 @@ impl Texture { graphics::draw( ctx, image, - param - .scale([x_scale, y_scale]) - .offset([0.5, 0.5]), + param.scale([x_scale, y_scale]).offset([0.5, 0.5]), )?; Ok(()) } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..c2add81 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,22 @@ +use std::path::{Path, PathBuf}; + +use anyhow::Result; + +pub fn fuck_you_windows( + parent: impl AsRef, + name: impl AsRef, +) -> Result> { + let parent = parent.as_ref(); + let name = name.as_ref(); + + let name_lower = name.to_ascii_lowercase(); + for entry in parent.read_dir()? { + let entry = entry?; + let entry_lower = entry.file_name().to_str().unwrap().to_ascii_lowercase(); + if name_lower == entry_lower { + return Ok(Some(entry.path())); + } + } + + Ok(None) +}