commit constructed sliders to hit objects
This commit is contained in:
parent
b8752dfd51
commit
9a31622002
4 changed files with 112 additions and 30 deletions
|
@ -1,2 +1 @@
|
||||||
pub struct Game {
|
pub struct Game {}
|
||||||
}
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
mod game;
|
||||||
|
mod renderer;
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
|
135
src/game/mod.rs
135
src/game/mod.rs
|
@ -44,6 +44,13 @@ pub const DEFAULT_COLORS: &[(f32, f32, f32)] = &[
|
||||||
|
|
||||||
pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>;
|
pub type SliderCache = HashMap<Vec<Point<i32>>, Spline>;
|
||||||
|
|
||||||
|
pub struct PartialSliderState {
|
||||||
|
start_time: TimestampMillis,
|
||||||
|
kind: SliderSplineKind,
|
||||||
|
control_points: Vec<Point<i32>>,
|
||||||
|
pixel_length: f64,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Tool {
|
pub enum Tool {
|
||||||
Select,
|
Select,
|
||||||
|
@ -65,7 +72,7 @@ pub struct Game {
|
||||||
combo_colors: Vec<Color>,
|
combo_colors: Vec<Color>,
|
||||||
selected_objects: Vec<usize>,
|
selected_objects: Vec<usize>,
|
||||||
tool: Tool,
|
tool: Tool,
|
||||||
partial_slider_state: Option<(SliderSplineKind, Vec<Point<i32>>)>,
|
partial_slider_state: Option<PartialSliderState>,
|
||||||
|
|
||||||
keymap: HashSet<KeyCode>,
|
keymap: HashSet<KeyCode>,
|
||||||
mouse_pos: (f32, f32),
|
mouse_pos: (f32, f32),
|
||||||
|
@ -198,7 +205,13 @@ impl Game {
|
||||||
|
|
||||||
let time = self.song.as_ref().unwrap().position()?;
|
let time = self.song.as_ref().unwrap().position()?;
|
||||||
let time_millis = TimestampMillis((time * 1000.0) as i32);
|
let time_millis = TimestampMillis((time * 1000.0) as i32);
|
||||||
let text = Text::new(format!("time: {:.4}, mouse: {:?}", time, self.mouse_pos).as_ref());
|
let text = Text::new(
|
||||||
|
format!(
|
||||||
|
"tool: {:?} time: {:.4}, mouse: {:?}",
|
||||||
|
self.tool, time, self.mouse_pos
|
||||||
|
)
|
||||||
|
.as_ref(),
|
||||||
|
);
|
||||||
graphics::queue_text(ctx, &text, [0.0, 0.0], Some(WHITE));
|
graphics::queue_text(ctx, &text, [0.0, 0.0], Some(WHITE));
|
||||||
graphics::draw_queued_text(ctx, DrawParam::default(), None, FilterMode::Linear)?;
|
graphics::draw_queued_text(ctx, DrawParam::default(), None, FilterMode::Linear)?;
|
||||||
|
|
||||||
|
@ -407,34 +420,31 @@ impl Game {
|
||||||
}
|
}
|
||||||
Tool::Slider => {
|
Tool::Slider => {
|
||||||
let color = Color::new(1.0, 1.0, 1.0, 0.4);
|
let color = Color::new(1.0, 1.0, 1.0, 0.4);
|
||||||
if let Some((kind, nodes)) = &self.partial_slider_state {
|
if let Some(state) = &mut self.partial_slider_state {
|
||||||
let mut nodes2 = nodes.clone();
|
let mut nodes = state.control_points.clone();
|
||||||
let mut kind = *kind;
|
let mut kind = state.kind;
|
||||||
if let Some(last) = nodes2.last() {
|
if let Some(last) = nodes.last() {
|
||||||
if mouse_pos != *last {
|
if mouse_pos != *last {
|
||||||
nodes2.push(mouse_pos);
|
nodes.push(mouse_pos);
|
||||||
if kind == SliderSplineKind::Linear && nodes2.len() == 3 {
|
kind = upgrade_slider_type(kind, nodes.len());
|
||||||
kind = SliderSplineKind::Perfect;
|
|
||||||
} else if kind == SliderSplineKind::Perfect && nodes2.len() == 4 {
|
|
||||||
kind = SliderSplineKind::Bezier;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if nodes2.len() > 1 && !(nodes2.len() == 2 && nodes2[0] == nodes2[1]) {
|
if nodes.len() > 1 && !(nodes.len() == 2 && nodes[0] == nodes[1]) {
|
||||||
let slider_velocity =
|
let slider_velocity =
|
||||||
self.beatmap.inner.get_slider_velocity_at_time(time_millis);
|
self.beatmap.inner.get_slider_velocity_at_time(time_millis);
|
||||||
let slider_multiplier = self.beatmap.inner.difficulty.slider_multiplier;
|
let slider_multiplier = self.beatmap.inner.difficulty.slider_multiplier;
|
||||||
let pixels_per_beat = slider_multiplier * 100.0 * slider_velocity;
|
let pixels_per_beat = slider_multiplier * 100.0 * slider_velocity;
|
||||||
let pixels_per_tick = pixels_per_beat / 4.0; // TODO: FIX!!!
|
let pixels_per_tick = pixels_per_beat / 4.0; // TODO: FIX!!!
|
||||||
|
|
||||||
let mut spline = Spline::from_control(kind, &nodes2, None);
|
let mut spline = Spline::from_control(kind, &nodes, None);
|
||||||
let len = spline.pixel_length();
|
let len = spline.pixel_length();
|
||||||
debug!("original len: {}", len);
|
debug!("original len: {}", len);
|
||||||
let num_ticks = (len / pixels_per_tick).floor();
|
let num_ticks = (len / pixels_per_tick).floor();
|
||||||
debug!("num ticks: {}", num_ticks);
|
debug!("num ticks: {}", num_ticks);
|
||||||
|
|
||||||
let fixed_len = num_ticks * pixels_per_tick;
|
let fixed_len = num_ticks * pixels_per_tick;
|
||||||
|
state.pixel_length = fixed_len;
|
||||||
debug!("fixed len: {}", fixed_len);
|
debug!("fixed len: {}", fixed_len);
|
||||||
spline.truncate(fixed_len);
|
spline.truncate(fixed_len);
|
||||||
|
|
||||||
|
@ -449,7 +459,7 @@ impl Game {
|
||||||
debug!("done rendering slider body");
|
debug!("done rendering slider body");
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::render_slider_wireframe(ctx, &nodes2, PLAYFIELD_BOUNDS)?;
|
Game::render_slider_wireframe(ctx, &nodes, PLAYFIELD_BOUNDS)?;
|
||||||
debug!("done rendering slider wireframe");
|
debug!("done rendering slider wireframe");
|
||||||
} else {
|
} else {
|
||||||
if rect_contains(&PLAYFIELD_BOUNDS, mx, my) {
|
if rect_contains(&PLAYFIELD_BOUNDS, mx, my) {
|
||||||
|
@ -556,6 +566,9 @@ impl Game {
|
||||||
|
|
||||||
if let Some(song) = &self.song {
|
if let Some(song) = &self.song {
|
||||||
println!("song exists! {:?} {:?}", btn, self.tool);
|
println!("song exists! {:?} {:?}", btn, self.tool);
|
||||||
|
let time = song.position()?;
|
||||||
|
let time_millis = TimestampMillis((time * 1000.0) as i32);
|
||||||
|
|
||||||
if let (MouseButton::Left, Tool::Select) = (btn, &self.tool) {
|
if let (MouseButton::Left, Tool::Select) = (btn, &self.tool) {
|
||||||
} else if let (MouseButton::Left, Tool::Circle) = (btn, &self.tool) {
|
} else if let (MouseButton::Left, Tool::Circle) = (btn, &self.tool) {
|
||||||
println!("left, circle, {:?} {} {}", PLAYFIELD_BOUNDS, x, y);
|
println!("left, circle, {:?} {} {}", PLAYFIELD_BOUNDS, x, y);
|
||||||
|
@ -573,7 +586,6 @@ impl Game {
|
||||||
use libosu::{
|
use libosu::{
|
||||||
hitobject::HitObject,
|
hitobject::HitObject,
|
||||||
hitsounds::{Additions, SampleInfo},
|
hitsounds::{Additions, SampleInfo},
|
||||||
timing::TimestampMillis,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let inner = HitObject {
|
let inner = HitObject {
|
||||||
|
@ -600,24 +612,85 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if let (MouseButton::Left, Tool::Slider) = (btn, &self.tool) {
|
} else if let (MouseButton::Left, Tool::Slider) = (btn, &self.tool) {
|
||||||
if let Some((ref mut kind, ref mut nodes)) = &mut self.partial_slider_state {
|
if let Some(PartialSliderState {
|
||||||
|
kind,
|
||||||
|
control_points: ref mut nodes,
|
||||||
|
..
|
||||||
|
}) = &mut self.partial_slider_state
|
||||||
|
{
|
||||||
nodes.push(pos);
|
nodes.push(pos);
|
||||||
if *kind == SliderSplineKind::Linear && nodes.len() == 3 {
|
*kind = upgrade_slider_type(*kind, nodes.len());
|
||||||
*kind = SliderSplineKind::Perfect;
|
|
||||||
} else if *kind == SliderSplineKind::Perfect && nodes.len() == 4 {
|
|
||||||
*kind = SliderSplineKind::Bezier;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.partial_slider_state = Some((SliderSplineKind::Linear, vec![pos]));
|
self.partial_slider_state = Some(PartialSliderState {
|
||||||
|
start_time: time_millis,
|
||||||
|
kind: SliderSplineKind::Linear,
|
||||||
|
control_points: vec![pos],
|
||||||
|
pixel_length: 0.0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if let (MouseButton::Right, Tool::Slider) = (btn, &self.tool) {
|
} else if let (MouseButton::Right, Tool::Slider) = (btn, &self.tool) {
|
||||||
if let Some(_) = &mut self.partial_slider_state {
|
if let Some(state) = &mut self.partial_slider_state {
|
||||||
|
match self
|
||||||
|
.beatmap
|
||||||
|
.hit_objects
|
||||||
|
.binary_search_by_key(&state.start_time.0, |ho| ho.inner.start_time.0)
|
||||||
|
{
|
||||||
|
Ok(v) => {
|
||||||
|
println!("unfortunately already found at idx {}", v);
|
||||||
|
}
|
||||||
|
Err(idx) => {
|
||||||
|
use libosu::{
|
||||||
|
hitobject::{HitObject, SliderInfo},
|
||||||
|
hitsounds::{Additions, SampleInfo},
|
||||||
|
};
|
||||||
|
|
||||||
|
state.control_points.push(pos);
|
||||||
|
let after_len = state.control_points.len();
|
||||||
|
let first = state.control_points.remove(0);
|
||||||
|
let inner = HitObject {
|
||||||
|
start_time: state.start_time,
|
||||||
|
pos: first,
|
||||||
|
kind: HitObjectKind::Slider(SliderInfo {
|
||||||
|
kind: upgrade_slider_type(state.kind, after_len),
|
||||||
|
control_points: state.control_points.clone(),
|
||||||
|
num_repeats: 1,
|
||||||
|
pixel_length: state.pixel_length,
|
||||||
|
edge_additions: vec![],
|
||||||
|
edge_samplesets: vec![],
|
||||||
|
}),
|
||||||
|
new_combo: false,
|
||||||
|
skip_color: 0,
|
||||||
|
additions: Additions::empty(),
|
||||||
|
sample_info: SampleInfo::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_obj = HitObjectExt {
|
||||||
|
inner,
|
||||||
|
stacking: 0,
|
||||||
|
color_idx: 0,
|
||||||
|
number: 0,
|
||||||
|
};
|
||||||
|
println!("creating new hitobject: {:?}", new_obj);
|
||||||
|
self.beatmap.hit_objects.insert(idx, new_obj);
|
||||||
|
self.beatmap.compute_stacking();
|
||||||
|
self.beatmap.compute_colors(&self.combo_colors);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.partial_slider_state = None;
|
self.partial_slider_state = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn switch_tool_to(&mut self, target: Tool) {
|
||||||
|
// clear slider state if we're switching away from slider
|
||||||
|
if matches!(self.tool, Tool::Slider) && !matches!(target, Tool::Slider) {
|
||||||
|
self.partial_slider_state = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tool = target;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventHandler for Game {
|
impl EventHandler for Game {
|
||||||
|
@ -685,9 +758,9 @@ impl EventHandler for Game {
|
||||||
use KeyCode::*;
|
use KeyCode::*;
|
||||||
self.keymap.insert(keycode);
|
self.keymap.insert(keycode);
|
||||||
match keycode {
|
match keycode {
|
||||||
Key1 => self.tool = Tool::Select,
|
Key1 => self.switch_tool_to(Tool::Select),
|
||||||
Key2 => self.tool = Tool::Circle,
|
Key2 => self.switch_tool_to(Tool::Circle),
|
||||||
Key3 => self.tool = Tool::Slider,
|
Key3 => self.switch_tool_to(Tool::Slider),
|
||||||
|
|
||||||
Left => {
|
Left => {
|
||||||
if let Some(TimingPoint {
|
if let Some(TimingPoint {
|
||||||
|
@ -728,3 +801,11 @@ impl EventHandler for Game {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upgrade_slider_type(initial_type: SliderSplineKind, after_len: usize) -> SliderSplineKind {
|
||||||
|
match (initial_type, after_len) {
|
||||||
|
(SliderSplineKind::Linear, 3) => SliderSplineKind::Perfect,
|
||||||
|
(SliderSplineKind::Perfect, 4) => SliderSplineKind::Bezier,
|
||||||
|
_ => initial_type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue