From dad5f77816198b2e6b5d764ea36f60e18b2e1f64 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Sat, 9 Jan 2021 16:33:08 -0600 Subject: [PATCH] seeker + make slider nodes red --- src/audio.rs | 8 ++ src/game/mod.rs | 19 ++-- src/game/seeker.rs | 42 ++++++++ src/game/sliders.rs | 239 +++++++++++++++++++++++--------------------- 4 files changed, 190 insertions(+), 118 deletions(-) create mode 100644 src/game/seeker.rs diff --git a/src/audio.rs b/src/audio.rs index c942652..21e1987 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -69,6 +69,14 @@ impl Sound { self.handle } + pub fn length(&self) -> Result { + let length = unsafe { + let bytes = bass::BASS_ChannelGetLength(self.handle, BASS_POS_BYTE); + bass::BASS_ChannelBytes2Seconds(self.handle, bytes) + }; + Ok(length) + } + pub fn position(&self) -> Result { let time = unsafe { let pos_bytes = bass::BASS_ChannelGetPosition(self.handle, BASS_POS_BYTE); diff --git a/src/game/mod.rs b/src/game/mod.rs index eccc01e..98bc211 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -1,4 +1,5 @@ mod grid; +mod seeker; mod sliders; mod timeline; @@ -25,10 +26,7 @@ use crate::beatmap::{BeatmapExt, STACK_DISTANCE}; use crate::hitobject::HitObjectExt; use crate::skin::Skin; -use self::sliders::render_slider; - pub const PLAYFIELD_BOUNDS: Rect = Rect::new(112.0, 112.0, 800.0, 600.0); -pub const SEEKER_BOUNDS: Rect = Rect::new(46.0, 722.0, 932.0, 36.0); pub type SliderCache = HashMap>, Spline>; @@ -185,17 +183,22 @@ impl Game { let mut slider_info = None; if let HitObjectKind::Slider(info) = &ho.inner.kind { + let mut control_points = vec![ho.inner.pos]; + control_points.extend(&info.control_points); + let mut color = color.clone(); color.a = 0.6 * draw_info.opacity as f32; - let spline = render_slider( + let spline = Game::render_slider( &mut self.slider_cache, + info, + control_points.as_ref(), ctx, PLAYFIELD_BOUNDS, &self.beatmap.inner, &ho.inner, color, )?; - slider_info = Some((info, spline)); + slider_info = Some((info, control_points, spline)); let end_pos = ho.inner.end_pos().unwrap(); let end_pos = [ @@ -225,7 +228,9 @@ impl Game { DrawParam::default().dest(pos), )?; - if let Some((info, spline)) = slider_info { + if let Some((info, control_points, spline)) = slider_info { + Game::render_slider_wireframe(ctx, &control_points, PLAYFIELD_BOUNDS)?; + if time > ho_time && time < draw_info.end_time { let elapsed_time = time - ho_time; let total_duration = draw_info.end_time - ho_time; @@ -263,6 +268,8 @@ impl Game { } } + self.draw_seeker(ctx)?; + graphics::present(ctx)?; self.frame += 1; Ok(()) diff --git a/src/game/seeker.rs b/src/game/seeker.rs new file mode 100644 index 0000000..3235ce8 --- /dev/null +++ b/src/game/seeker.rs @@ -0,0 +1,42 @@ +use anyhow::Result; +use ggez::{ + graphics::{self, DrawParam, Mesh, Rect}, + nalgebra::Point2, + Context, +}; + +use super::Game; + +pub const BOUNDS: Rect = Rect::new(46.0, 722.0, 932.0, 36.0); + +impl Game { + pub(super) fn draw_seeker(&self, ctx: &mut Context) -> Result<()> { + let line_y = BOUNDS.y + BOUNDS.h / 2.0; + let line = Mesh::new_line( + ctx, + &[ + Point2::new(BOUNDS.x, line_y), + Point2::new(BOUNDS.x + BOUNDS.w, line_y), + ], + 1.0, + graphics::WHITE, + )?; + graphics::draw(ctx, &line, DrawParam::default())?; + + if let Some(song) = &self.song { + let percent = song.position()? / song.length()?; + let x = BOUNDS.x + percent as f32 * BOUNDS.w; + let line = Mesh::new_line( + ctx, + &[ + Point2::new(x, BOUNDS.y + 0.2 * BOUNDS.h), + Point2::new(x, BOUNDS.y + 0.8 * BOUNDS.h), + ], + 4.0, + graphics::WHITE, + )?; + graphics::draw(ctx, &line, DrawParam::default())?; + } + Ok(()) + } +} diff --git a/src/game/sliders.rs b/src/game/sliders.rs index d03ec0a..7d029c3 100644 --- a/src/game/sliders.rs +++ b/src/game/sliders.rs @@ -1,6 +1,5 @@ use anyhow::Result; use ggez::{ - conf::NumSamples, graphics::{ self, Canvas, Color, DrawMode, DrawParam, FillOptions, LineCap, LineJoin, Mesh, Rect, StrokeOptions, @@ -10,127 +9,143 @@ use ggez::{ }; use libosu::{ beatmap::Beatmap, - hitobject::{HitObject, HitObjectKind}, + hitobject::{HitObject, SliderInfo}, + math::Point, spline::Spline, }; -use crate::game::SliderCache; +use super::{Game, SliderCache}; -pub fn render_slider<'a>( - slider_cache: &'a mut SliderCache, - ctx: &mut Context, - rect: Rect, - beatmap: &Beatmap, - slider: &HitObject, - color: Color, -) -> Result<&'a Spline> { - let mut control_points = vec![slider.pos]; - let slider_info = match &slider.kind { - HitObjectKind::Slider(info) => info, - _ => unreachable!("retard"), - }; - control_points.extend(&slider_info.control_points); +impl Game { + pub fn render_slider<'a>( + slider_cache: &'a mut SliderCache, + slider_info: &SliderInfo, + control_points: &[Point], + ctx: &mut Context, + rect: Rect, + beatmap: &Beatmap, + slider: &HitObject, + color: Color, + ) -> Result<&'a Spline> { + let spline = if slider_cache.contains_key(control_points) { + slider_cache.get(control_points).unwrap() + } else { + let new_spline = + Spline::from_control(slider_info.kind, control_points, slider_info.pixel_length); + slider_cache.insert(control_points.to_vec(), new_spline); + slider_cache.get(control_points).unwrap() + }; - let spline = if slider_cache.contains_key(&control_points) { - slider_cache.get(&control_points).unwrap() - } else { - let new_spline = - Spline::from_control(slider_info.kind, &control_points, slider_info.pixel_length); - slider_cache.insert(control_points.clone(), new_spline); - slider_cache.get(&control_points).unwrap() - }; + let cs_scale = rect.w / 640.0; + let osupx_scale_x = rect.w as f64 / 512.0; + let osupx_scale_y = rect.h as f64 / 384.0; + let cs_osupx = beatmap.difficulty.circle_size_osupx() as f64; + let cs_real = cs_osupx * cs_scale as f64; - let cs_scale = rect.w / 640.0; - let osupx_scale_x = rect.w as f64 / 512.0; - let osupx_scale_y = rect.h as f64 / 384.0; - let cs_osupx = beatmap.difficulty.circle_size_osupx() as f64; - let cs_real = cs_osupx * cs_scale as f64; + let (mut boundx, mut boundy, mut boundw, mut boundh) = (f64::MAX, f64::MAX, 0.0f64, 0.0f64); + let spline_mapped = spline + .spline_points + .iter() + .map(|point| { + let (x, y) = (point.0, point.1); + let x2 = rect.x as f64 + osupx_scale_x * x; + let y2 = rect.y as f64 + osupx_scale_y * y; + boundx = boundx.min(x2 - cs_osupx); + boundy = boundy.min(y2 - cs_osupx); + boundw = boundw.max(x2 + cs_osupx - boundx); + boundh = boundh.max(y2 + cs_osupx - boundy); + [x2 as f32, y2 as f32].into() + }) + .collect::>>(); - let points_mapped = control_points - .iter() - .map(|point| { - let (x, y) = (point.0 as f64, point.1 as f64); - let x2 = rect.x as f64 + osupx_scale_x * x; - let y2 = rect.y as f64 + osupx_scale_y * y; - [x2 as f32, y2 as f32].into() - }) - .collect::>>(); - - let (mut boundx, mut boundy, mut boundw, mut boundh) = (f64::MAX, f64::MAX, 0.0f64, 0.0f64); - let spline_mapped = spline - .spline_points - .iter() - .map(|point| { - let (x, y) = (point.0, point.1); - let x2 = rect.x as f64 + osupx_scale_x * x; - let y2 = rect.y as f64 + osupx_scale_y * y; - boundx = boundx.min(x2 - cs_osupx); - boundy = boundy.min(y2 - cs_osupx); - boundw = boundw.max(x2 + cs_osupx - boundx); - boundh = boundh.max(y2 + cs_osupx - boundy); - [x2 as f32, y2 as f32].into() - }) - .collect::>>(); - - // draw slider border - let canvas = Canvas::with_window_size(ctx)?; - let opts = StrokeOptions::default() - .with_line_cap(LineCap::Round) - .with_line_join(LineJoin::Round) - .with_line_width(cs_real as f32 * 2.0); - let body = Mesh::new_polyline( - ctx, - DrawMode::Stroke(opts), - spline_mapped.as_ref(), - graphics::WHITE, - )?; - graphics::set_canvas(ctx, Some(&canvas)); - graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0)); - graphics::draw(ctx, &body, DrawParam::default())?; - graphics::set_canvas(ctx, None); - let mut border_color = graphics::WHITE; - border_color.a = color.a; - graphics::draw(ctx, &canvas, DrawParam::default().color(border_color))?; - - // draw slider body - let canvas = Canvas::with_window_size(ctx)?; - let opts = StrokeOptions::default() - .with_line_cap(LineCap::Round) - .with_line_join(LineJoin::Round) - .with_line_width(cs_real as f32 * 1.8); - let body = Mesh::new_polyline( - ctx, - DrawMode::Stroke(opts), - spline_mapped.as_ref(), - graphics::WHITE, - )?; - graphics::set_canvas(ctx, Some(&canvas)); - graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0)); - graphics::draw(ctx, &body, DrawParam::default())?; - graphics::set_canvas(ctx, None); - graphics::draw(ctx, &canvas, DrawParam::default().color(color))?; - - // draw control points wireframe - let frame = Mesh::new_polyline( - ctx, - DrawMode::Stroke(StrokeOptions::default()), - &points_mapped, - graphics::WHITE, - )?; - graphics::draw(ctx, &frame, DrawParam::default())?; - - // draw points on wireframe - for point in points_mapped { - let size = 5.0; - let rect = Rect::new(point.x - size, point.y - size, size * 2.0, size * 2.0); - let rect = Mesh::new_rectangle( + // draw slider border + let canvas = Canvas::with_window_size(ctx)?; + let opts = StrokeOptions::default() + .with_line_cap(LineCap::Round) + .with_line_join(LineJoin::Round) + .with_line_width(cs_real as f32 * 2.0); + let body = Mesh::new_polyline( ctx, - DrawMode::Fill(FillOptions::default()), - rect, + DrawMode::Stroke(opts), + spline_mapped.as_ref(), graphics::WHITE, )?; - graphics::draw(ctx, &rect, DrawParam::default())?; + graphics::set_canvas(ctx, Some(&canvas)); + graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0)); + graphics::draw(ctx, &body, DrawParam::default())?; + graphics::set_canvas(ctx, None); + let mut border_color = graphics::WHITE; + border_color.a = color.a; + graphics::draw(ctx, &canvas, DrawParam::default().color(border_color))?; + + // draw slider body + let canvas = Canvas::with_window_size(ctx)?; + let opts = StrokeOptions::default() + .with_line_cap(LineCap::Round) + .with_line_join(LineJoin::Round) + .with_line_width(cs_real as f32 * 1.8); + let body = Mesh::new_polyline( + ctx, + DrawMode::Stroke(opts), + spline_mapped.as_ref(), + graphics::WHITE, + )?; + graphics::set_canvas(ctx, Some(&canvas)); + graphics::clear(ctx, Color::new(0.0, 0.0, 0.0, 0.0)); + graphics::draw(ctx, &body, DrawParam::default())?; + graphics::set_canvas(ctx, None); + graphics::draw(ctx, &canvas, DrawParam::default().color(color))?; + + Ok(spline) } - Ok(spline) + pub fn render_slider_wireframe( + ctx: &mut Context, + control_points: &[Point], + rect: Rect, + ) -> Result<()> { + let osupx_scale_x = rect.w as f64 / 512.0; + let osupx_scale_y = rect.h as f64 / 384.0; + + let points_mapped = control_points + .iter() + .map(|point| { + let (x, y) = (point.0 as f64, point.1 as f64); + let x2 = rect.x as f64 + osupx_scale_x * x; + let y2 = rect.y as f64 + osupx_scale_y * y; + [x2 as f32, y2 as f32].into() + }) + .collect::>>(); + + // draw control points wireframe + let frame = Mesh::new_polyline( + ctx, + DrawMode::Stroke(StrokeOptions::default()), + &points_mapped, + graphics::WHITE, + )?; + graphics::draw(ctx, &frame, DrawParam::default())?; + + // draw points on wireframe + let mut i = 0; + while i < points_mapped.len() { + let fst = points_mapped[i]; + let mut color = graphics::WHITE; + if i < points_mapped.len() - 1 { + let snd = points_mapped[i + 1]; + if fst.eq(&snd) { + i += 1; + color = Color::new(1.0, 0.0, 0.0, 1.0); + } + } + let size = 5.0; + let rect = Rect::new(fst.x - size, fst.y - size, size * 2.0, size * 2.0); + let rect = + Mesh::new_rectangle(ctx, DrawMode::Fill(FillOptions::default()), rect, color)?; + graphics::draw(ctx, &rect, DrawParam::default())?; + i += 1; + } + + Ok(()) + } }